Skip to content

Commit

Permalink
Merge branch '106-arbitrary-tx-add-type' into 'dev'
Browse files Browse the repository at this point in the history
Resolve "Arbitrary Tx: Add type"

Closes #106

See merge request ergo/rosen-bridge/rosen-chains!147
  • Loading branch information
zargarzadehm committed Oct 26, 2024
2 parents 29daab9 + 81f3103 commit 6c4f4f5
Show file tree
Hide file tree
Showing 14 changed files with 233 additions and 93 deletions.
5 changes: 5 additions & 0 deletions .changeset/stupid-ducks-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rosen-chains/abstract-chain': minor
---

add order encoder and decoder
6 changes: 6 additions & 0 deletions .changeset/wicked-ears-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@rosen-chains/abstract-chain': major
'@rosen-chains/ergo': major
---

add arbitrary transaction type
2 changes: 2 additions & 0 deletions packages/abstract-chain/lib/AbstractChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,8 @@ abstract class AbstractChain<TxType> {
return this.configs.confirmations.observation;
case TransactionType.manual:
return this.configs.confirmations.manual;
case TransactionType.arbitrary:
return this.configs.confirmations.arbitrary;
default:
throw Error(
`Confirmation for type [${transactionType}] is not defined in abstract chain`
Expand Down
32 changes: 31 additions & 1 deletion packages/abstract-chain/lib/ChainUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AssetBalance, TokenInfo } from './types';
import { AssetBalance, PaymentOrder, TokenInfo } from './types';
import { ValueError } from './errors';
import { TokenMap } from '@rosen-bridge/tokens';
import JsonBigInt from '@rosen-bridge/json-bigint';

class ChainUtils {
/**
Expand Down Expand Up @@ -159,6 +160,35 @@ class ChainUtils {
);
return result;
};

/**
* encodes the order to string
* token placement in single payment should not affect the encoding
* @param order
*/
static encodeOrder = (order: PaymentOrder): string => {
const organizedOrder = order.map((payment) => {
const organizedPayment = structuredClone(payment);
organizedPayment.assets.tokens.sort((a, b) => {
if (a.id === b.id) {
if (a.value === b.value) return 0;
else if (a.value > b.value) return 1;
else return -1;
} else return a.id > b.id ? 1 : -1;
});
return organizedPayment;
});

return JsonBigInt.stringify(organizedOrder);
};

/**
* decodes the order from string
* @param encodedOrder
*/
static decodeOrder = (encodedOrder: string): PaymentOrder => {
return JsonBigInt.parse(encodedOrder);
};
}

export default ChainUtils;
2 changes: 2 additions & 0 deletions packages/abstract-chain/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ interface ConfirmationConfigs {
payment: number;
cold: number;
manual: number;
arbitrary: number;
}

interface AddressConfigs {
Expand Down Expand Up @@ -100,6 +101,7 @@ enum TransactionType {
coldStorage = 'cold-storage',
lock = 'lock',
manual = 'manual',
arbitrary = 'arbitrary',
}

interface TokenDetail {
Expand Down
96 changes: 95 additions & 1 deletion packages/abstract-chain/tests/ChainUtils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { TokenMap } from '@rosen-bridge/tokens';
import { AssetBalance, ChainUtils, ValueError } from '../lib';
import { AssetBalance, ChainUtils, PaymentOrder, ValueError } from '../lib';
import {
actualBalance,
encodedOrder,
organizedOrder,
sortedTokens,
testTokenMap,
unorganizedAssetBalance,
unorganizedOrder,
unwrappedBalance,
wrappedBalance,
} from './testData';
import JsonBigInt from '@rosen-bridge/json-bigint';

describe('ChainUtils', () => {
describe('isEqualAssetBalance', () => {
Expand Down Expand Up @@ -650,4 +656,92 @@ describe('ChainUtils', () => {
expect(result).toEqual(unwrappedBalance);
});
});

describe('encodeOrder', () => {
/**
* @target ChainUtils.encodeOrder should NOT mutate the tokens
* @dependencies
* @scenario
* - mock an AssetBalance with unorganized tokens
* - run test
* - check returned value
* @expected
* - the tokens list should be sorted
* - the original order token list should remain unchanged
*/
it('should NOT mutate the tokens', () => {
// mock an AssetBalance with unorganized tokens
const a = unorganizedAssetBalance;
const order: PaymentOrder = [
{
address: 'addr2',
assets: a,
},
];
const originalTokens = structuredClone(a.tokens);

// run test
const result = ChainUtils.encodeOrder(order);

// check returned value
const organizedOrder: PaymentOrder = JsonBigInt.parse(result);
expect(organizedOrder[0].assets).toEqual({
nativeToken: a.nativeToken,
tokens: sortedTokens,
});
expect(order[0].assets.tokens).toEqual(originalTokens);
});
});

/**
* @target ChainUtils.encodeOrder should sort the tokens before encoding
* @dependencies
* @scenario
* - mock two AssetBalance with unorganized tokens
* - run test
* - check returned value
* @expected
* - order should remain the same
* - the tokens list of each record should be sorted
*/
it('should sort the tokens before encoding', () => {
// mock two AssetBalance with unorganized tokens
const order: PaymentOrder = unorganizedOrder;

// run test
const result = ChainUtils.encodeOrder(order);

// check returned value
const organizedOrder: PaymentOrder = JsonBigInt.parse(result);
expect(organizedOrder[0].address).toEqual('addr2');
expect(organizedOrder[0].assets).toEqual({
nativeToken: unorganizedAssetBalance.nativeToken,
tokens: sortedTokens,
});
expect(organizedOrder[1].address).toEqual('addr1');
expect(organizedOrder[1].assets).toEqual({
nativeToken: 100n,
tokens: sortedTokens,
});
});

describe('decodeOrder', () => {
/**
* @target ChainUtils.decodeOrder should decode the order successfully
* @dependencies
* @scenario
* - run test
* - check returned value
* @expected
* - order should remain the same
* - the tokens list of each record should be sorted
*/
it('should decode the order successfully', () => {
// run test
const result = ChainUtils.decodeOrder(encodedOrder);

// check returned value
expect(result).toEqual(organizedOrder);
});
});
});
60 changes: 59 additions & 1 deletion packages/abstract-chain/tests/testData.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RosenTokens } from '@rosen-bridge/tokens';
import { AssetBalance, EventTrigger } from '../lib';
import { AssetBalance, EventTrigger, PaymentOrder, TokenInfo } from '../lib';

export const paymentTxConfirmation = 6;

Expand Down Expand Up @@ -223,3 +223,61 @@ export const unwrappedBalance: AssetBalance = {
},
],
};
export const originalTokens = [
{
id: 'id2',
value: 20n,
},
{
id: 'id1',
value: 10n,
},
];
export const unorganizedAssetBalance: AssetBalance = {
nativeToken: 200n,
tokens: originalTokens,
};
export const unorganizedAssetBalance2: AssetBalance = {
nativeToken: 100n,
tokens: structuredClone(originalTokens),
};
export const sortedTokens: TokenInfo[] = [
{
id: 'id1',
value: 10n,
},
{
id: 'id2',
value: 20n,
},
];
export const unorganizedOrder: PaymentOrder = [
{
address: 'addr2',
assets: unorganizedAssetBalance,
},
{
address: 'addr1',
assets: {
nativeToken: 100n,
tokens: structuredClone(originalTokens),
},
},
];
export const organizedOrder: PaymentOrder = [
{
address: 'addr2',
assets: {
nativeToken: unorganizedAssetBalance.nativeToken,
tokens: structuredClone(sortedTokens),
},
},
{
address: 'addr1',
assets: {
nativeToken: 100n,
tokens: structuredClone(sortedTokens),
},
},
];
export const encodedOrder = `[{"address":"addr2","assets":{"nativeToken":200,"tokens":[{"id":"id1","value":10},{"id":"id2","value":20}]}},{"address":"addr1","assets":{"nativeToken":100,"tokens":[{"id":"id1","value":10},{"id":"id2","value":20}]}}]`;
2 changes: 2 additions & 0 deletions packages/abstract-chain/tests/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const generateChainObject = (network: TestChainNetwork) => {
payment: testData.paymentTxConfirmation,
cold: 7,
manual: 8,
arbitrary: 9,
},
addresses: {
lock: 'lock_addr',
Expand All @@ -36,6 +37,7 @@ export const generateUtxoChainObject = (network: TestUtxoChainNetwork) => {
payment: 6,
cold: 7,
manual: 8,
arbitrary: 9,
},
addresses: {
lock: 'lock_addr',
Expand Down
2 changes: 2 additions & 0 deletions packages/chains/bitcoin/tests/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const observationTxConfirmation = 5;
export const paymentTxConfirmation = 9;
export const coldTxConfirmation = 10;
export const manualTxConfirmation = 11;
export const arbitraryTxConfirmation = 12;
export const rwtId =
'9410db5b39388c6b515160e7248346d7ec63d5457292326da12a26cc02efb526';
export const configs: BitcoinConfigs = {
Expand All @@ -25,6 +26,7 @@ export const configs: BitcoinConfigs = {
payment: paymentTxConfirmation,
cold: coldTxConfirmation,
manual: manualTxConfirmation,
arbitrary: arbitraryTxConfirmation,
},
aggregatedPublicKey: testData.lockAddressPublicKey,
txFeeSlippage: 10,
Expand Down
2 changes: 2 additions & 0 deletions packages/chains/cardano/tests/testUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export const observationTxConfirmation = 5;
export const paymentTxConfirmation = 9;
export const coldTxConfirmation = 10;
export const manualTxConfirmation = 11;
export const arbitraryTxConfirmation = 12;
export const rwtId =
'9410db5b39388c6b515160e7248346d7ec63d5457292326da12a26cc02efb526';
export const minBoxValue = 2000000n;
Expand All @@ -147,6 +148,7 @@ export const configs: CardanoConfigs = {
payment: paymentTxConfirmation,
cold: coldTxConfirmation,
manual: manualTxConfirmation,
arbitrary: arbitraryTxConfirmation,
},
aggregatedPublicKey:
'bcb07faa6c0f19e2f2587aa9ef6f43a68fc0135321216a71dc87c8527af4ca6a',
Expand Down
2 changes: 2 additions & 0 deletions packages/chains/ergo/lib/ErgoChain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,8 @@ class ErgoChain extends AbstractUtxoChain<wasm.Transaction, wasm.ErgoBox> {
return this.configs.confirmations.observation;
case TransactionType.manual:
return this.configs.confirmations.manual;
case TransactionType.arbitrary:
return this.configs.confirmations.arbitrary;
default:
throw Error(
`Confirmation for type [${transactionType}] is not defined in Ergo chain`
Expand Down
Loading

0 comments on commit 6c4f4f5

Please sign in to comment.