Skip to content

Commit

Permalink
Merge branch '104-decimals-drop-evm' into 'dev'
Browse files Browse the repository at this point in the history
consider decimals drop in EvmChain

Closes #104

See merge request ergo/rosen-bridge/rosen-chains!121
  • Loading branch information
vorujack committed Jul 27, 2024
2 parents 56f40f3 + a269d1a commit 648bb7e
Show file tree
Hide file tree
Showing 5 changed files with 331 additions and 14 deletions.
5 changes: 5 additions & 0 deletions .changeset/light-fans-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rosen-chains/evm': major
---

consider decimals drop
61 changes: 47 additions & 14 deletions packages/chains/evm/lib/EvmChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,22 +127,32 @@ abstract class EvmChain extends AbstractChain<Transaction> {
for (const singleOrder of orders) {
let trx;
if (singleOrder.assets.nativeToken !== 0n) {
const value = this.tokenMap.unwrapAmount(
this.NATIVE_TOKEN_ID,
singleOrder.assets.nativeToken,
this.CHAIN
).amount;
trx = Transaction.from({
type: 2,
to: singleOrder.address,
nonce: nextNonce,
maxPriorityFeePerGas: maxPriorityFeePerGas,
maxFeePerGas: gasPrice,
data: '0x' + eventId,
value: singleOrder.assets.nativeToken,
value: value,
chainId: this.CHAIN_ID,
});
} else {
const token = singleOrder.assets.tokens[0];
const tokenValue = this.tokenMap.unwrapAmount(
token.id,
token.value,
this.CHAIN
).amount;
const data = EvmUtils.encodeTransferCallData(
token.id,
singleOrder.address,
token.value
tokenValue
);

trx = Transaction.from({
Expand Down Expand Up @@ -175,11 +185,14 @@ abstract class EvmChain extends AbstractChain<Transaction> {

// check the balance in the lock address
const requiredAssets: AssetBalance = orders.reduce(
(sum: AssetBalance, order: SinglePayment) => {
return ChainUtils.sumAssetBalance(sum, order.assets);
},
(sum: AssetBalance, order: SinglePayment) =>
ChainUtils.sumAssetBalance(sum, order.assets),
{
nativeToken: totalGas * gasPrice,
nativeToken: this.tokenMap.wrapAmount(
this.NATIVE_TOKEN_ID,
totalGas * gasPrice,
this.CHAIN
).amount,
tokens: [],
}
);
Expand Down Expand Up @@ -246,10 +259,16 @@ abstract class EvmChain extends AbstractChain<Transaction> {
});
}

const wrappedAssets = ChainUtils.wrapAssetBalance(
assets,
this.tokenMap,
this.NATIVE_TOKEN_ID,
this.CHAIN
);
// no need to calculate outputAssets separately, they are always equal in account-based
return {
inputAssets: assets,
outputAssets: structuredClone(assets),
inputAssets: wrappedAssets,
outputAssets: structuredClone(wrappedAssets),
};
};

Expand Down Expand Up @@ -306,7 +325,15 @@ abstract class EvmChain extends AbstractChain<Transaction> {
}
}
}
return payment;
return payment.map((singleOrder) => {
singleOrder.assets = ChainUtils.wrapAssetBalance(
singleOrder.assets,
this.tokenMap,
this.NATIVE_TOKEN_ID,
this.CHAIN
);
return singleOrder;
});
};

/**
Expand Down Expand Up @@ -480,7 +507,7 @@ abstract class EvmChain extends AbstractChain<Transaction> {
* checks the following conditions before:
* - transaction must of of type 2
* - fees are set appropriately according to the current network's condition
* - lock address stil have enough funds
* - lock address still have enough funds
* @param transaction the transaction
*/
submitTransaction = async (
Expand Down Expand Up @@ -723,10 +750,16 @@ abstract class EvmChain extends AbstractChain<Transaction> {
};
})
);
return {
nativeToken: nativeTokenBalance,
tokens: tokens,
};
const wrappedAssets = ChainUtils.wrapAssetBalance(
{
nativeToken: nativeTokenBalance,
tokens: tokens,
},
this.tokenMap,
this.NATIVE_TOKEN_ID,
this.CHAIN
);
return wrappedAssets;
};
}

Expand Down
203 changes: 203 additions & 0 deletions packages/chains/evm/tests/EvmChain.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,79 @@ describe('EvmChain', () => {
);
}).not.rejects;
});

/**
* @target EvmChain.generateMultipleTransactions should generate payment
* transaction successfully for wrapped order
* @dependencies
* @scenario
* - mock hasLockAddressEnoughAssets, getMaxFeePerGas
* - mock getGasRequired, getAddressNextNonce
* - mock getMaxPriorityFeePerGas
* - call the function
* - check returned value
* @expected
* - PaymentTransaction txType, eventId and network should be as
* expected
* - extracted order of generated transaction should be the same as input
* order
* - eventId should be properly in the transaction data
* - no extra data should be found in the transaction data
* - transaction must be of type 2 and has no blobs
* - nonce must be the same as the next available nonce
*/
it('should generate payment transaction successfully for wrapped order', async () => {
const evmChain =
testUtils.generateChainObjectWithMultiDecimalTokenMap(network);

const order = TestData.nativePaymentWrappedOrder;
const eventId = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb';
const txType = TransactionType.payment;
const nonce = 54;

// mock hasLockAddressEnoughAssets, getMaxFeePerGas,
// getGasRequired, getAddressNextNonce, getMaxPriorityFeePerGas
testUtils.mockHasLockAddressEnoughAssets(evmChain, true);
testUtils.mockGetMaxFeePerGas(network, 10n);
testUtils.mockGetGasRequired(network, 21000n);
testUtils.mockGetAddressNextAvailableNonce(network, nonce);
testUtils.mockGetMaxPriorityFeePerGas(network, 10n);

// run test
const evmTx = await evmChain.generateMultipleTransactions(
eventId,
txType,
order,
[],
[]
);

// check returned value
expect(evmTx[0].txType).toEqual(txType);
expect(evmTx[0].eventId).toEqual(eventId);
expect(evmTx[0].network).toEqual(evmChain.CHAIN);

// extracted order of generated transaction should be the same as input order
const extractedOrder = evmChain.extractTransactionOrder(evmTx[0]);
expect(extractedOrder).toEqual(order);

const tx = Serializer.deserialize(evmTx[0].txBytes);

// check eventId encoded at the end of the data
expect(tx.data.substring(2, 34)).toEqual(eventId);

// check there is no more data
expect(tx.data.length).toEqual(34);

// check transaction type
expect(tx.type).toEqual(2);

// check blobs zero
expect(tx.maxFeePerBlobGas).toEqual(null);

// check nonce
expect(tx.nonce).toEqual(nonce);
});
});

describe('rawTxToPaymentTransaction', () => {
Expand Down Expand Up @@ -513,6 +586,45 @@ describe('EvmChain', () => {
await evmChain.getTransactionAssets(paymentTx);
}).rejects.toThrowError(TransactionFormatError);
});

/**
* @target EvmChain.getTransactionAssets should wrap transaction assets
* successfully
* @dependencies
* @scenario
* - mock PaymentTransaction
* - call the function
* - check returned value
* @expected
* - it should return mocked transaction assets (both input and output assets)
*/
it('should wrap transaction assets successfully', async () => {
const evmChain =
testUtils.generateChainObjectWithMultiDecimalTokenMap(network);

const eventId = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb';
const txType = TransactionType.payment;
// mock PaymentTransaction
const trx = { ...TestData.transaction1Json };
trx.data = '0x';
const tx = Transaction.from(trx);
const assets = { ...TestData.transaction1WrappedAssets };
assets.tokens = [];
const paymentTx = new PaymentTransaction(
evmChain.CHAIN,
tx.unsignedHash,
eventId,
Serializer.serialize(tx),
txType
);

// check returned value
const result = await evmChain.getTransactionAssets(paymentTx);

// check returned value
expect(result.inputAssets).toEqual(assets);
expect(result.outputAssets).toEqual(assets);
});
});

describe('verifyTransactionFee', () => {
Expand Down Expand Up @@ -1102,6 +1214,52 @@ describe('EvmChain', () => {
evmChain.extractTransactionOrder(paymentTx)
).rejects.toThrowError(TransactionFormatError);
});

/**
* @target EvmChain.extractTransactionOrder should wrap transaction
* order successfully
* @dependencies
* @scenario
* - mock PaymentTransaction
* - run test
* - check returned value
* @expected
* - it should return mocked transaction order
*/
it('should wrap transaction order successfully', () => {
const evmChain =
testUtils.generateChainObjectWithMultiDecimalTokenMap(network);

// mock PaymentTransaction
const eventId = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb';
const txType = TransactionType.payment;
const tx = Transaction.from({
...(TestData.erc20transaction as TransactionLike),
});
tx.data = '0x';
tx.value = 100n;
const paymentTx = new PaymentTransaction(
evmChain.CHAIN,
tx.unsignedHash,
eventId,
Serializer.serialize(tx),
txType
);

// run test
const result = evmChain.extractTransactionOrder(paymentTx);

// check returned value
expect(result).toEqual([
{
address: '0xedee4752e5a2f595151c94762fb38e5730357785',
assets: {
nativeToken: 1n,
tokens: [],
},
},
]);
});
});

describe('submitTransaction', () => {
Expand Down Expand Up @@ -1886,5 +2044,50 @@ describe('EvmChain', () => {
// check returned value
expect(result).toEqual({ nativeToken: 0n, tokens: [] });
});

/**
* @target EvmChain.getAddressAssets should wrap address assets successfully
* @dependencies
* @scenario
* - mock getAddressBalanceForNativeToken
* - mock getAddressBalanceForERC20Asset for each supported tokens
* - call the function
* - check returned value
* @expected
* - it should return mocked address assets (both input and output assets)
*/
it('should wrap address assets successfully', async () => {
const evmChain =
testUtils.generateChainObjectWithMultiDecimalTokenMap(network);

mockGetAddressBalanceForNativeToken(evmChain.network, 1000n);
vi.spyOn(network, 'getAddressBalanceForERC20Asset').mockImplementation(
async (address, tokenId) => {
if (tokenId === '0xedee4752e5a2f595151c94762fb38e5730357785')
return 0n;
else if (tokenId === '0x12345752e5a2f595151c94762fb38e5730357785')
return 10n;
else if (tokenId === '0xedee4752e5a2f595151c94762fb38e5730357786')
return 30n;
else if (tokenId === '0xedee4752e5a2f595151c94762fb38e5730357787')
return 40n;
else return 0n;
}
);

// run test
const result = await evmChain.getAddressAssets(TestData.lockAddress);

// check returned value
expect(result).toEqual({
nativeToken: 10n,
tokens: [
{ id: '0xedee4752e5a2f595151c94762fb38e5730357785', value: 0n },
{ id: '0x12345752e5a2f595151c94762fb38e5730357785', value: 10n },
{ id: '0xedee4752e5a2f595151c94762fb38e5730357786', value: 30n },
{ id: '0xedee4752e5a2f595151c94762fb38e5730357787', value: 40n },
],
});
});
});
});
13 changes: 13 additions & 0 deletions packages/chains/evm/tests/TestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,16 @@ export const generateChainObject = (
signFn
);
};

export const generateChainObjectWithMultiDecimalTokenMap = (
network: TestEvmNetwork,
signFn: TssSignFunction = mockedSignFn
) => {
return new TestChain(
network,
configs,
testData.multiDecimalTokenMap,
testData.supportedTokens,
signFn
);
};
Loading

0 comments on commit 648bb7e

Please sign in to comment.