From 4b4724c8579923aa6313c30efa37162af6a84923 Mon Sep 17 00:00:00 2001 From: canonbrother Date: Tue, 3 Oct 2023 17:48:24 +0800 Subject: [PATCH] chore(): fix regression of `4.0.0 beta13` (#2159) #### What this PR does / why we need it: #### Which issue(s) does this PR fixes?: Fixes # #### Additional comments?: --- .../__sanity__/WhaleSanityContainer.sanity.ts | 1 - apps/whale-api/docker-compose.yml | 3 +- contracts/TransferDomain.sol | 76 -- contracts/TransferDomainV1.sol | 126 +++ .../category/account/transferDomain.test.ts | 79 +- ...xn_builder_account_transfer_domain.test.ts | 906 +++++++++--------- .../src/containers/DeFiDContainer.ts | 2 +- .../src/containers/NativeChainContainer.ts | 2 +- 8 files changed, 639 insertions(+), 556 deletions(-) delete mode 100644 contracts/TransferDomain.sol create mode 100644 contracts/TransferDomainV1.sol diff --git a/apps/whale-api/__sanity__/WhaleSanityContainer.sanity.ts b/apps/whale-api/__sanity__/WhaleSanityContainer.sanity.ts index db14363fd..c767e5b52 100644 --- a/apps/whale-api/__sanity__/WhaleSanityContainer.sanity.ts +++ b/apps/whale-api/__sanity__/WhaleSanityContainer.sanity.ts @@ -164,7 +164,6 @@ describe('/rpc/getblock', () => { difficulty: 4.656542373906925e-10, hash: 'd744db74fb70ed42767ae028a129365fb4d7de54ba1b6575fb047490554f8a7b', height: 0, - masternode: '0000000000000000000000000000000000000000000000000000000000000000', mediantime: 1579045065, merkleroot: '5615dbbb379da893dd694e02d25a7955e1b7471db55f42bbd82b5d3f5bdb8d38', mintedBlocks: 0, diff --git a/apps/whale-api/docker-compose.yml b/apps/whale-api/docker-compose.yml index cad2bb09b..23c007a55 100644 --- a/apps/whale-api/docker-compose.yml +++ b/apps/whale-api/docker-compose.yml @@ -2,7 +2,8 @@ version: "3.7" services: defi-blockchain: - image: defi/defichain:master-b352814976 + image: defi/defichain:4.0.0-beta13 + ports: - "19554:19554" command: > diff --git a/contracts/TransferDomain.sol b/contracts/TransferDomain.sol deleted file mode 100644 index a06cadac3..000000000 --- a/contracts/TransferDomain.sol +++ /dev/null @@ -1,76 +0,0 @@ -// File: @openzeppelin/contracts@4.9.2/token/ERC20/IERC20.sol - -// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) - -pragma solidity ^0.8.0; - -interface IERC20 { - function transferFrom(address from, address to, uint256 amount) external returns (bool); -} - -// SPDX-License-Identifier: MIT - -pragma solidity >=0.8.2 <0.9.0; - -/** - * @title TransferDomain - */ -contract TransferDomain { - event Transfer(address indexed from, address indexed to, uint256 amount); - event NativeAddress(string nativeAddress); - - function transfer(address from, address payable to, uint256 amount, string memory nativeAddress) external { - if (to != address(this)) { - require(address(this).balance >= amount, "Insufficient contract balance"); - to.transfer(amount); - } - - emit Transfer(from, to, amount); - emit NativeAddress(nativeAddress); - } - - /** - * @dev Returns the name of the token. - */ - function name() public view virtual returns (string memory) { - return "DFI"; - } - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() public view virtual returns (string memory) { - return "DFI"; - } - - /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5.05` (`505 / 10 ** 2`). - * - * Tokens usually opt for a value of 18, imitating the relationship between - * Ether and Wei. This is the default value returned by this function, unless - * it's overridden. - * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * {IERC20-balanceOf} and {IERC20-transfer}. - */ - function decimals() public view virtual returns (uint8) { - return 18; - } - - function bridgeDST20( - address contractAddress, - address from, - address payable to, - uint256 amount, - string memory nativeAddress - ) external { - if (to != address(this)) { - IERC20(contractAddress).transferFrom(from, to, amount); - } - emit NativeAddress(nativeAddress); - } -} diff --git a/contracts/TransferDomainV1.sol b/contracts/TransferDomainV1.sol new file mode 100644 index 000000000..1b341c0ab --- /dev/null +++ b/contracts/TransferDomainV1.sol @@ -0,0 +1,126 @@ +// File: @openzeppelin/contracts@4.9.2/token/ERC20/IERC20.sol + +// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface for the optional metadata functions from the ERC20 standard. + * + * _Available since v4.1._ + */ +interface IERC20Metadata { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); +} + +interface IERC20 { + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); +} + +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/** + * @title TransferDomain + */ +contract TransferDomainV1 is IERC20Metadata { + mapping(address => uint256) private _balances; + uint256 private _totalSupply; + + event Transfer(address indexed from, address indexed to, uint256 amount); + event VMTransfer(string vmAddress); + + function transfer( + address from, + address payable to, + uint256 amount, + string memory vmAddress + ) external { + if (to != address(this)) { + require( + address(this).balance >= amount, + "Insufficient contract balance" + ); + + to.transfer(amount); + } + + emit Transfer(from, to, amount); + emit VMTransfer(vmAddress); + } + + /** + * @dev Returns the name of the token. + */ + function name() public view virtual override returns (string memory) { + return "TransferDomain"; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view virtual override returns (string memory) { + return "DFI"; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5.05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the default value returned by this function, unless + * it's overridden. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view virtual override returns (uint8) { + return 18; + } + + function transferDST20( + address contractAddress, + address from, + address payable to, + uint256 amount, + string memory vmAddress + ) external { + IERC20(contractAddress).transferFrom(from, to, amount); + emit VMTransfer(vmAddress); + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view virtual returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual returns (uint256) { + return _balances[account]; + } +} diff --git a/packages/jellyfish-api-core/__tests__/category/account/transferDomain.test.ts b/packages/jellyfish-api-core/__tests__/category/account/transferDomain.test.ts index c6c3e51ee..963ad0135 100644 --- a/packages/jellyfish-api-core/__tests__/category/account/transferDomain.test.ts +++ b/packages/jellyfish-api-core/__tests__/category/account/transferDomain.test.ts @@ -218,7 +218,7 @@ describe('TransferDomain', () => { } ]) await expect(promise).rejects.toThrow(RpcApiError) - await expect(promise).rejects.toThrow(`Failed to create and sign TX: Invalid address ${dvmAddr}`) + await expect(promise).rejects.toThrow('Failed to create and sign TX: Invalid address') }) it('(evm -> dvm) should fail if src address is not ERC55 address in case of "EVM" domain', async () => { @@ -486,7 +486,7 @@ describe('TransferDomain', () => { } ]) await expect(promise).rejects.toThrow(RpcApiError) - await expect(promise).rejects.toThrow(`Failed to create and sign TX: Invalid address ${dvmAddr}`) + await expect(promise).rejects.toThrow('Failed to create and sign TX: Invalid address') }) it('(evm -> evm) should fail if transfer within same domain', async () => { @@ -527,9 +527,8 @@ describe('TransferDomain', () => { await expect(promise).rejects.toThrow('amount 90000.00000000 is less than 999999.00000000') }) - // TODO(canonbrother): check again here after pull the later version - it.skip('(evm -> dvm) should fail if insufficient balance', async () => { - const promise = client.account.transferDomain([ + it('(evm -> dvm) should not fail if insufficient balance but tx remained in mempool', async () => { + const txid = await client.account.transferDomain([ { src: { address: evmAddr, @@ -543,8 +542,12 @@ describe('TransferDomain', () => { } } ]) - await expect(promise).rejects.toThrow(RpcApiError) - await expect(promise).rejects.toThrow(`Not enough balance in ${evmAddr} to cover "EVM" domain transfer`) + const mempool: string[] = await container.call('getrawmempool') + await container.generate(1) + const found = mempool.find((m: string) => m === txid) + expect(found).toBeDefined() + + await container.call('clearmempool') }) it('(dvm -> evm) should fail if custom (isDAT = false) token is transferred', async () => { @@ -729,37 +732,6 @@ describe('TransferDomain', () => { .toStrictEqual(new BigNumber(currentBalance).minus(3)) }) - it('(dvm -> evm) should transfer domain - dToken', async () => { - const dvmAcc = await getAccountValues(client, dvmAddr) - const btcTokenId = 'BTC' - const btcBalance = dvmAcc[btcTokenId] - const txid1 = await client.account.transferDomain([ - { - src: { - address: dvmAddr, - amount: '3@BTC', - domain: TransferDomainType.DVM - }, - dst: { - address: evmAddr, - amount: '3@BTC', - domain: TransferDomainType.EVM - } - } - ]) - expect(typeof txid1).toStrictEqual('string') - expect(txid1.length).toStrictEqual(64) - - await container.generate(1) - - const dvmAcc1 = await getAccountValues(client, dvmAddr) - const btcBalance1 = dvmAcc1[btcTokenId] - - // check: BTC balance is transferred - expect(new BigNumber(btcBalance1)) - .toStrictEqual(new BigNumber(btcBalance).minus(3)) - }) - it('(evm -> dvm) should transfer domain - DFI', async () => { const dvmAcc = await getAccountValues(client, dvmAddr) const tokenId = 'DFI' @@ -795,6 +767,37 @@ describe('TransferDomain', () => { .toStrictEqual(new BigNumber(currentBalance).plus(3)) }) + it('(dvm -> evm) should transfer domain - dToken', async () => { + const dvmAcc = await getAccountValues(client, dvmAddr) + const btcTokenId = 'BTC' + const btcBalance = dvmAcc[btcTokenId] + const txid1 = await client.account.transferDomain([ + { + src: { + address: dvmAddr, + amount: '3@BTC', + domain: TransferDomainType.DVM + }, + dst: { + address: evmAddr, + amount: '3@BTC', + domain: TransferDomainType.EVM + } + } + ]) + expect(typeof txid1).toStrictEqual('string') + expect(txid1.length).toStrictEqual(64) + + await container.generate(1) + + const dvmAcc1 = await getAccountValues(client, dvmAddr) + const btcBalance1 = dvmAcc1[btcTokenId] + + // check: BTC balance is transferred + expect(new BigNumber(btcBalance1)) + .toStrictEqual(new BigNumber(btcBalance).minus(3)) + }) + it('(evm -> dvm) should transfer domain - dToken', async () => { const dvmAcc = await getAccountValues(client, dvmAddr) const btcTokenId = 'BTC' diff --git a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_account_transfer_domain.test.ts b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_account_transfer_domain.test.ts index da292a6b1..f326ddabb 100644 --- a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_account_transfer_domain.test.ts +++ b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_account_transfer_domain.test.ts @@ -13,9 +13,10 @@ import { } from '@defichain/jellyfish-transaction' import { WIF } from '@defichain/jellyfish-crypto' import { P2WPKH } from '@defichain/jellyfish-address' -import TransferDomainSol from '../../../../artifacts/contracts/TransferDomain.sol/TransferDomain.json' +import TransferDomainV1 from '../../../../artifacts/contracts/TransferDomainV1.sol/TransferDomainV1.json' +import waitForExpect from 'wait-for-expect' -const TD_CONTRACT_ADDR = '0x0000000000000000000000000000000000000302' +const TD_CONTRACT_ADDR = '0xdf00000000000000000000000000000000000001' const DST_20_CONTRACT_ADDR_BTC = '0xff00000000000000000000000000000000000001' const TRANSFER_DOMAIN_TYPE = { @@ -75,7 +76,7 @@ describe('transferDomain', () => { const evmPrivKey = await testing.container.call('dumpprivkey', [evmAddr]) wallet = new ethers.Wallet(evmPrivKey) - tdFace = new ethers.Interface(TransferDomainSol.abi) + tdFace = new ethers.Interface(TransferDomainV1.abi) evmRpcUrl = await testing.container.getCachedEvmRpcUrl() rpc = new ethers.JsonRpcProvider(evmRpcUrl) @@ -124,15 +125,16 @@ describe('transferDomain', () => { const amount = '0x8ac7230489e80000' // 10_000_000_000_000_000_000 const native = dvmAddr const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) const tx: ethers.TransactionRequest = { to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, + nonce: nonce, data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee + chainId: (await rpc.getNetwork()).chainId, + value: 0, + gasLimit: 0, + gasPrice: 0 } const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` @@ -180,15 +182,16 @@ describe('transferDomain', () => { const amount = '0x1bc16d674ec80000' // 2_000_000_000_000_000_000 const native = dvmAddr const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) const tx: ethers.TransactionRequest = { to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, + nonce: nonce, data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee + chainId: (await rpc.getNetwork()).chainId, + value: 0, + gasLimit: 0, + gasPrice: 0 } const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` @@ -236,15 +239,16 @@ describe('transferDomain', () => { const amount = '0x29a2241af62c0000' // 3_000_000_000_000_000_000 const native = dvmAddr const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) const tx: ethers.TransactionRequest = { to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, + nonce: nonce, data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee + chainId: (await rpc.getNetwork()).chainId, + value: 0, + gasLimit: 0, + gasPrice: 0 } const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` @@ -292,15 +296,16 @@ describe('transferDomain', () => { const amount = '0x29a2241af62c0000' // 3_000_000_000_000_000_000 const native = dvmAddr const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) const tx: ethers.TransactionRequest = { to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, + nonce: nonce, data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee + chainId: (await rpc.getNetwork()).chainId, + value: 0, + gasLimit: 0, + gasPrice: 0 } const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` @@ -348,15 +353,16 @@ describe('transferDomain', () => { const amount = '0x29a2241af62c0000' // 3_000_000_000_000_000_000 const native = dvmAddr const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) const tx: ethers.TransactionRequest = { to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, + nonce: nonce, data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee + chainId: (await rpc.getNetwork()).chainId, + value: 0, + gasLimit: 0, + gasPrice: 0 } const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` @@ -404,15 +410,16 @@ describe('transferDomain', () => { const amount = '0x29a2241af62c0000' // 3_000_000_000_000_000_000 const native = dvmAddr const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) const tx: ethers.TransactionRequest = { to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, + nonce: nonce, data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee + chainId: (await rpc.getNetwork()).chainId, + value: 0, + gasLimit: 0, + gasPrice: 0 } const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` @@ -460,15 +467,16 @@ describe('transferDomain', () => { const amount = '0x29a2241af62c0000' // 3_000_000_000_000_000_000 const native = dvmAddr const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) const tx: ethers.TransactionRequest = { to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, + nonce: nonce, data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee + chainId: (await rpc.getNetwork()).chainId, + value: 0, + gasLimit: 0, + gasPrice: 0 } const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` @@ -518,15 +526,16 @@ describe('transferDomain', () => { const amount = '0x29a2241af62c0000' // 3_000_000_000_000_000_000 const native = dvmAddr const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) const tx: ethers.TransactionRequest = { to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, + nonce: nonce, data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee + chainId: (await rpc.getNetwork()).chainId, + value: 0, + gasLimit: 0, + gasPrice: 0 } const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` @@ -565,204 +574,337 @@ describe('transferDomain', () => { await expect(promise).rejects.toThrow(DeFiDRpcError) await expect(promise).rejects.toThrow('Non-DAT or LP tokens are not supported for transferdomain') }) - }) - it('should transfer domain from DVM to EVM', async () => { - const dvmAccBefore = await testing.rpc.account.getAccount(dvmAddr) - const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[0].split('@') - const prevBalance = await getEVMBalances(testing) + it('should fail (duo) transfer domain from DVM to EVM', async () => { + let evmTx = new Uint8Array([]) + { + // EvmIn + const from = evmAddr + const to = evmAddr + const amount = '0x1bc16d674ec80000' // 2_000_000_000_000_000_000 + const native = dvmAddr + const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) - let evmTx = new Uint8Array([]) - { - // EvmIn - const from = evmAddr - const to = evmAddr - const amount = '0x29a2241af62c0000' // 3_000_000_000_000_000_000 - const native = dvmAddr - const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const tx: ethers.TransactionRequest = { + to: TD_CONTRACT_ADDR, + nonce: nonce, + data: data, + chainId: (await rpc.getNetwork()).chainId, + value: 0, + gasLimit: 0, + gasPrice: 0 + } - const tx: ethers.TransactionRequest = { - to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, - data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee - } + const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` - const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` + evmTx = new Uint8Array(Buffer.from(signed, 'hex')) + } - evmTx = new Uint8Array(Buffer.from(signed, 'hex')) - } + let evmTx1 = new Uint8Array([0]) + { + // EvmIn + const from = evmAddr + const to = evmAddr + const amount = '0x14d1120d7b160000' // 1_500_000_000_000_000_000 + const native = dvmAddr + const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) - const transferDomain: TransferDomain = { - items: [{ - src: - { - address: dvmScript, - domain: TRANSFER_DOMAIN_TYPE.DVM, - amount: { - token: 0, - amount: new BigNumber(3) - }, - data: new Uint8Array([]) - }, - dst: { - address: evmScript, - domain: TRANSFER_DOMAIN_TYPE.EVM, - amount: { - token: 0, - amount: new BigNumber(3) - }, - data: evmTx + const tx: ethers.TransactionRequest = { + to: TD_CONTRACT_ADDR, + nonce: nonce, + data: data, + chainId: (await rpc.getNetwork()).chainId, + value: 0, + gasLimit: 0, + gasPrice: 0 } - }] - } - // NOTE(canonbrother): `maximumAmount` is a workaround to grab only single vin - // since maximumCount behaviour does not return by provided value - // but catch up total utxos of the tokenId - const txn = await builder.account.transferDomain(transferDomain, dvmScript, { maximumAmount: 50 }) - const outs = await sendTransaction(container, txn) - const encoded: string = OP_CODES.OP_DEFI_TX_TRANSFER_DOMAIN(transferDomain).asBuffer().toString('hex') - const expectedTransferDomainScript = `6a${encoded}` - expect(outs.length).toStrictEqual(2) - expect(outs[0].value).toStrictEqual(0) - expect(outs[0].n).toStrictEqual(0) - expect(outs[0].tokenId).toStrictEqual(0) - expect(outs[0].scriptPubKey.asm.startsWith('OP_RETURN 4466547838')).toStrictEqual(true) - expect(outs[0].scriptPubKey.hex).toStrictEqual(expectedTransferDomainScript) - expect(outs[0].scriptPubKey.type).toStrictEqual('nulldata') + const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` - expect(outs[1].value).toEqual(expect.any(Number)) - expect(outs[1].n).toStrictEqual(1) - expect(outs[1].tokenId).toStrictEqual(0) - expect(outs[1].scriptPubKey.type).toStrictEqual('witness_v0_keyhash') - expect(outs[1].scriptPubKey.addresses[0]).toStrictEqual(dvmAddr) + evmTx1 = new Uint8Array(Buffer.from(signed, 'hex')) + } - const dvmAccAfter = await testing.rpc.account.getAccount(dvmAddr) - const [dvmBalanceAfter0, tokenIdAfter0] = dvmAccAfter[0].split('@') - expect(tokenIdBefore0).toStrictEqual(tokenIdAfter0) + const transferDomain: TransferDomain = { + items: [{ + src: { + address: dvmScript, + domain: TRANSFER_DOMAIN_TYPE.DVM, + amount: { + token: 0, + amount: new BigNumber(2) + }, + data: new Uint8Array([]) + }, + dst: { + address: evmScript, + domain: TRANSFER_DOMAIN_TYPE.EVM, + amount: { + token: 0, + amount: new BigNumber(2) + }, + data: evmTx + } + }, { + src: + { + address: dvmScript, + domain: TRANSFER_DOMAIN_TYPE.DVM, + amount: { + token: 0, + amount: new BigNumber(1.5) + }, + data: new Uint8Array([]) + }, + dst: { + address: evmScript, + domain: TRANSFER_DOMAIN_TYPE.EVM, + amount: { + token: 0, + amount: new BigNumber(1.5) + }, + data: evmTx1 + } + }] + } - // check: dvm balance is transferred - expect(new BigNumber(dvmBalanceAfter0)) - .toStrictEqual(new BigNumber(dvmBalanceBefore0).minus(3)) + const txn = await builder.account.transferDomain(transferDomain, dvmScript, { maximumAmount: 50 }) + const promise = sendTransaction(testing.container, txn) - // check: evm balance = dvm balance - transferred - const currentBalance = await getEVMBalances(testing) - expect(new BigNumber(prevBalance)) - .toStrictEqual(new BigNumber(currentBalance).minus(3)) - }) + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow('TransferDomain currently only supports a single transfer per transaction') + }) - // TODO(canonbrother): flaky - it.skip('should transfer domain from EVM to DVM', async () => { - const dvmAccBefore = await testing.rpc.account.getAccount(dvmAddr) - const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[0].split('@') - const prevBalance = await getEVMBalances(testing) + it('should fail (duo) transfer domain from EVM to DVM', async () => { + let evmTx = new Uint8Array([]) + { + // EvmOut + const from = evmAddr + const to = TD_CONTRACT_ADDR + const amount = '0x1bc16d674ec80000' // 2_000_000_000_000_000_000 + const native = dvmAddr + const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) - let evmTx = new Uint8Array([]) - { - // EvmOut - const from = evmAddr - const to = TD_CONTRACT_ADDR - const amount = '0x29a2241af62c0000' // 3_000_000_000_000_000_000 - const native = dvmAddr - const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const tx: ethers.TransactionRequest = { + to: TD_CONTRACT_ADDR, + nonce: nonce, + data: data, + chainId: (await rpc.getNetwork()).chainId, + value: 0, + gasLimit: 0, + gasPrice: 0 + } - const tx: ethers.TransactionRequest = { - to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, - data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee - } + const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` - const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` + evmTx = new Uint8Array(Buffer.from(signed, 'hex')) + } - evmTx = new Uint8Array(Buffer.from(signed, 'hex')) - } + let evmTx1 = new Uint8Array([0]) + { + // EvmOut + const from = evmAddr + const to = TD_CONTRACT_ADDR + const amount = '0x14d1120d7b160000' // 1_500_000_000_000_000_000 + const native = dvmAddr + const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) - const transferDomain: TransferDomain = { - items: [{ - src: - { - address: evmScript, - domain: TRANSFER_DOMAIN_TYPE.EVM, - amount: { - token: 0, - amount: new BigNumber(3) - }, - data: evmTx - }, - dst: { - address: dvmScript, - domain: TRANSFER_DOMAIN_TYPE.DVM, - amount: { - token: 0, - amount: new BigNumber(3) - }, - data: new Uint8Array([]) + const tx: ethers.TransactionRequest = { + to: TD_CONTRACT_ADDR, + nonce: nonce, + data: data, + chainId: (await rpc.getNetwork()).chainId, + value: 0, + gasLimit: 0, + gasPrice: 0 } - }] - } - - const txn = await builder.account.transferDomain(transferDomain, dvmScript, { maximumAmount: 50 }) - const outs = await sendTransaction(container, txn) - const encoded: string = OP_CODES.OP_DEFI_TX_TRANSFER_DOMAIN(transferDomain).asBuffer().toString('hex') - const expectedTransferDomainScript = `6a${encoded}` - expect(outs.length).toStrictEqual(2) - expect(outs[0].value).toStrictEqual(0) - expect(outs[0].n).toStrictEqual(0) - expect(outs[0].tokenId).toStrictEqual(0) - expect(outs[0].scriptPubKey.asm.startsWith('OP_RETURN 4466547838')).toStrictEqual(true) - expect(outs[0].scriptPubKey.hex).toStrictEqual(expectedTransferDomainScript) - expect(outs[0].scriptPubKey.type).toStrictEqual('nulldata') + const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` - expect(outs[1].value).toEqual(expect.any(Number)) - expect(outs[1].n).toStrictEqual(1) - expect(outs[1].tokenId).toStrictEqual(0) - expect(outs[1].scriptPubKey.type).toStrictEqual('witness_v0_keyhash') - expect(outs[1].scriptPubKey.addresses[0]).toStrictEqual(dvmAddr) + evmTx1 = new Uint8Array(Buffer.from(signed, 'hex')) + } + const transferDomain: TransferDomain = { + items: [{ + src: + { + address: evmScript, + domain: TRANSFER_DOMAIN_TYPE.EVM, + amount: { + token: 0, + amount: new BigNumber(2) + }, + data: new Uint8Array([]) + }, + dst: { + address: dvmScript, + domain: TRANSFER_DOMAIN_TYPE.DVM, + amount: { + token: 0, + amount: new BigNumber(2) + }, + data: evmTx + } + }, + { + src: + { + address: evmScript, + domain: TRANSFER_DOMAIN_TYPE.EVM, + amount: { + token: 0, + amount: new BigNumber(1.5) + }, + data: new Uint8Array([]) + }, + dst: { + address: dvmScript, + domain: TRANSFER_DOMAIN_TYPE.DVM, + amount: { + token: 0, + amount: new BigNumber(1.5) + }, + data: evmTx1 + } + } + ] + } - const dvmAccAfter = await testing.rpc.account.getAccount(dvmAddr) - const [dvmBalanceAfter0, tokenIdAfter0] = dvmAccAfter[0].split('@') - expect(tokenIdBefore0).toStrictEqual(tokenIdAfter0) + const txn = await builder.account.transferDomain(transferDomain, dvmScript) + const promise = sendTransaction(testing.container, txn) - // check: dvm balance is updated - expect(new BigNumber(dvmBalanceAfter0)) - .toStrictEqual(new BigNumber(dvmBalanceBefore0).plus(3)) + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow('TransferDomain currently only supports a single transfer per transaction') + }) - // check evm balance to be equal to zero - const currentBalance = await getEVMBalances(testing) - expect(new BigNumber(prevBalance)) - .toStrictEqual(new BigNumber(currentBalance).plus(3)) + it('should fail (duo-diff) Transfer Domain from EVM to DVM and DVM to EVM', async () => { + let evmTx = new Uint8Array([]) + { + // EvmIn + const from = evmAddr + const to = evmAddr + const amount = '0x3782dace9d900000' // 4_000_000_000_000_000_000 + const native = dvmAddr + const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) + + const tx: ethers.TransactionRequest = { + to: TD_CONTRACT_ADDR, + nonce: nonce, + data: data, + chainId: (await rpc.getNetwork()).chainId, + value: 0, + gasLimit: 0, + gasPrice: 0 + } + + const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` + + evmTx = new Uint8Array(Buffer.from(signed, 'hex')) + } + + let evmTx1 = new Uint8Array([0]) + { + // EvmOut + const from = evmAddr + const to = TD_CONTRACT_ADDR + const amount = '0x29a2241af62c0000' // 3_000_000_000_000_000_000 + const native = dvmAddr + const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) + + const tx: ethers.TransactionRequest = { + to: TD_CONTRACT_ADDR, + nonce: nonce, + data: data, + chainId: (await rpc.getNetwork()).chainId, + value: 0, + gasLimit: 0, + gasPrice: 0 + } + + const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` + + evmTx1 = new Uint8Array(Buffer.from(signed, 'hex')) + } + const transferDomain: TransferDomain = { + items: [{ + src: + { + address: dvmScript, + domain: TRANSFER_DOMAIN_TYPE.DVM, + amount: { + token: 0, + amount: new BigNumber(4) + }, + data: new Uint8Array([]) + }, + dst: { + address: evmScript, + domain: TRANSFER_DOMAIN_TYPE.EVM, + amount: { + token: 0, + amount: new BigNumber(4) + }, + data: evmTx + } + }, + { + src: + { + address: evmScript, + domain: TRANSFER_DOMAIN_TYPE.EVM, + amount: { + token: 0, + amount: new BigNumber(3) + }, + data: new Uint8Array([]) + }, + dst: { + address: dvmScript, + domain: TRANSFER_DOMAIN_TYPE.DVM, + amount: { + token: 0, + amount: new BigNumber(3) + }, + data: evmTx1 + } + } + ] + } + + const txn = await builder.account.transferDomain(transferDomain, dvmScript, { maximumAmount: 50 }) + const promise = sendTransaction(testing.container, txn) + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow('TransferDomain currently only supports a single transfer per transaction') + }) }) - it('should transfer domain dToken from DVM to EVM', async () => { + it('should transfer domain from DVM to EVM', async () => { const dvmAccBefore = await testing.rpc.account.getAccount(dvmAddr) - const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[1].split('@') + const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[0].split('@') let evmTx = new Uint8Array([]) { // EvmIn - const from = evmAddr + const from = TD_CONTRACT_ADDR const to = evmAddr const amount = '0x29a2241af62c0000' // 3_000_000_000_000_000_000 const native = dvmAddr - const data = tdFace.encodeFunctionData('bridgeDST20', [DST_20_CONTRACT_ADDR_BTC, from, to, amount, native]) + const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) const tx: ethers.TransactionRequest = { to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, + nonce: nonce, data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee + chainId: (await rpc.getNetwork()).chainId, + type: 0, + value: 0, + gasLimit: 0, + gasPrice: 0 } const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` @@ -777,7 +919,7 @@ describe('transferDomain', () => { address: dvmScript, domain: TRANSFER_DOMAIN_TYPE.DVM, amount: { - token: 1, // <- BTC + token: 0, amount: new BigNumber(3) }, data: new Uint8Array([]) @@ -786,14 +928,16 @@ describe('transferDomain', () => { address: evmScript, domain: TRANSFER_DOMAIN_TYPE.EVM, amount: { - token: 1, // <- BTC + token: 0, amount: new BigNumber(3) }, data: evmTx } }] } - + // NOTE(canonbrother): `maximumAmount` is a workaround to grab only single vin + // since maximumCount behaviour does not return by provided value + // but catch up total utxos of the tokenId const txn = await builder.account.transferDomain(transferDomain, dvmScript, { maximumAmount: 50 }) const outs = await sendTransaction(container, txn) const encoded: string = OP_CODES.OP_DEFI_TX_TRANSFER_DOMAIN(transferDomain).asBuffer().toString('hex') @@ -814,7 +958,7 @@ describe('transferDomain', () => { expect(outs[1].scriptPubKey.addresses[0]).toStrictEqual(dvmAddr) const dvmAccAfter = await testing.rpc.account.getAccount(dvmAddr) - const [dvmBalanceAfter0, tokenIdAfter0] = dvmAccAfter[1].split('@') + const [dvmBalanceAfter0, tokenIdAfter0] = dvmAccAfter[0].split('@') expect(tokenIdBefore0).toStrictEqual(tokenIdAfter0) // check: dvm balance is transferred @@ -822,10 +966,15 @@ describe('transferDomain', () => { .toStrictEqual(new BigNumber(dvmBalanceBefore0).minus(3)) }) - // TODO(canonbrother): flaky - it.skip('should transfer domain dToken from EVM to DVM', async () => { + it('should transfer domain from EVM to DVM', async () => { + await waitForExpect(async () => { + const nonce = await rpc.getTransactionCount(evmAddr) + expect(nonce).toStrictEqual(1) + }, 100_000) + const dvmAccBefore = await testing.rpc.account.getAccount(dvmAddr) - const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[1].split('@') + const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[0].split('@') + const prevBalance = await getEVMBalances(testing) let evmTx = new Uint8Array([]) { @@ -834,16 +983,18 @@ describe('transferDomain', () => { const to = TD_CONTRACT_ADDR const amount = '0x29a2241af62c0000' // 3_000_000_000_000_000_000 const native = dvmAddr - const data = tdFace.encodeFunctionData('bridgeDST20', [DST_20_CONTRACT_ADDR_BTC, from, to, amount, native]) + const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) const tx: ethers.TransactionRequest = { to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, + nonce: nonce, data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee + chainId: (await rpc.getNetwork()).chainId, + type: 0, + value: 0, + gasLimit: 0, + gasPrice: 0 } const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` @@ -858,7 +1009,7 @@ describe('transferDomain', () => { address: evmScript, domain: TRANSFER_DOMAIN_TYPE.EVM, amount: { - token: 1, + token: 0, amount: new BigNumber(3) }, data: evmTx @@ -867,7 +1018,7 @@ describe('transferDomain', () => { address: dvmScript, domain: TRANSFER_DOMAIN_TYPE.DVM, amount: { - token: 1, + token: 0, amount: new BigNumber(3) }, data: new Uint8Array([]) @@ -895,91 +1046,63 @@ describe('transferDomain', () => { expect(outs[1].scriptPubKey.addresses[0]).toStrictEqual(dvmAddr) const dvmAccAfter = await testing.rpc.account.getAccount(dvmAddr) - const [dvmBalanceAfter0, tokenIdAfter0] = dvmAccAfter[1].split('@') + const [dvmBalanceAfter0, tokenIdAfter0] = dvmAccAfter[0].split('@') expect(tokenIdBefore0).toStrictEqual(tokenIdAfter0) // check: dvm balance is updated expect(new BigNumber(dvmBalanceAfter0)) .toStrictEqual(new BigNumber(dvmBalanceBefore0).plus(3)) - }) - it('should fail (duo) transfer domain from DVM to EVM', async () => { - let evmTx = new Uint8Array([]) - { - // EvmIn - const from = evmAddr - const to = evmAddr - const amount = '0x1bc16d674ec80000' // 2_000_000_000_000_000_000 - const native = dvmAddr - const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) - - const tx: ethers.TransactionRequest = { - to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, - data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee - } + // check evm balance to be equal to zero + const currentBalance = await getEVMBalances(testing) + expect(new BigNumber(prevBalance)) + .toStrictEqual(new BigNumber(currentBalance).plus(3)) + }) - const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` + it('should transfer domain dToken from DVM to EVM', async () => { + await waitForExpect(async () => { + const nonce = await rpc.getTransactionCount(evmAddr) + expect(nonce).toStrictEqual(2) + }, 100_000) - evmTx = new Uint8Array(Buffer.from(signed, 'hex')) - } + const dvmAccBefore = await testing.rpc.account.getAccount(dvmAddr) + const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[1].split('@') - let evmTx1 = new Uint8Array([0]) + let evmTx = new Uint8Array([]) { // EvmIn - const from = evmAddr + const from = TD_CONTRACT_ADDR const to = evmAddr - const amount = '0x14d1120d7b160000' // 1_500_000_000_000_000_000 + const amount = '0x29a2241af62c0000' // 3_000_000_000_000_000_000 const native = dvmAddr - const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const data = tdFace.encodeFunctionData('transferDST20', [DST_20_CONTRACT_ADDR_BTC, from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) const tx: ethers.TransactionRequest = { to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, + nonce: nonce, data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee + chainId: (await rpc.getNetwork()).chainId, + type: 0, + value: 0, + gasLimit: 0, + gasPrice: 0 } const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` - evmTx1 = new Uint8Array(Buffer.from(signed, 'hex')) + evmTx = new Uint8Array(Buffer.from(signed, 'hex')) } const transferDomain: TransferDomain = { items: [{ - src: { - address: dvmScript, - domain: TRANSFER_DOMAIN_TYPE.DVM, - amount: { - token: 0, - amount: new BigNumber(2) - }, - data: new Uint8Array([]) - }, - dst: { - address: evmScript, - domain: TRANSFER_DOMAIN_TYPE.EVM, - amount: { - token: 0, - amount: new BigNumber(2) - }, - data: evmTx - } - }, { src: { address: dvmScript, domain: TRANSFER_DOMAIN_TYPE.DVM, amount: { - token: 0, - amount: new BigNumber(1.5) + token: 1, // <- BTC + amount: new BigNumber(3) }, data: new Uint8Array([]) }, @@ -987,220 +1110,127 @@ describe('transferDomain', () => { address: evmScript, domain: TRANSFER_DOMAIN_TYPE.EVM, amount: { - token: 0, - amount: new BigNumber(1.5) + token: 1, // <- BTC + amount: new BigNumber(3) }, - data: evmTx1 + data: evmTx } }] } const txn = await builder.account.transferDomain(transferDomain, dvmScript, { maximumAmount: 50 }) - const promise = sendTransaction(testing.container, txn) - - await expect(promise).rejects.toThrow(DeFiDRpcError) - await expect(promise).rejects.toThrow('TransferDomain currently only supports a single transfer per transaction') - }) - - it('should fail (duo) transfer domain from EVM to DVM', async () => { - let evmTx = new Uint8Array([]) - { - // EvmOut - const from = evmAddr - const to = TD_CONTRACT_ADDR - const amount = '0x1bc16d674ec80000' // 2_000_000_000_000_000_000 - const native = dvmAddr - const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) - - const tx: ethers.TransactionRequest = { - to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, - data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee - } - - const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` - - evmTx = new Uint8Array(Buffer.from(signed, 'hex')) - } - - let evmTx1 = new Uint8Array([0]) - { - // EvmOut - const from = evmAddr - const to = TD_CONTRACT_ADDR - const amount = '0x14d1120d7b160000' // 1_500_000_000_000_000_000 - const native = dvmAddr - const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) - - const tx: ethers.TransactionRequest = { - to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, - data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee - } + const outs = await sendTransaction(container, txn) + const encoded: string = OP_CODES.OP_DEFI_TX_TRANSFER_DOMAIN(transferDomain).asBuffer().toString('hex') + const expectedTransferDomainScript = `6a${encoded}` - const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` + expect(outs.length).toStrictEqual(2) + expect(outs[0].value).toStrictEqual(0) + expect(outs[0].n).toStrictEqual(0) + expect(outs[0].tokenId).toStrictEqual(0) + expect(outs[0].scriptPubKey.asm.startsWith('OP_RETURN 4466547838')).toStrictEqual(true) + expect(outs[0].scriptPubKey.hex).toStrictEqual(expectedTransferDomainScript) + expect(outs[0].scriptPubKey.type).toStrictEqual('nulldata') - evmTx1 = new Uint8Array(Buffer.from(signed, 'hex')) - } - const transferDomain: TransferDomain = { - items: [{ - src: - { - address: evmScript, - domain: TRANSFER_DOMAIN_TYPE.EVM, - amount: { - token: 0, - amount: new BigNumber(2) - }, - data: new Uint8Array([]) - }, - dst: { - address: dvmScript, - domain: TRANSFER_DOMAIN_TYPE.DVM, - amount: { - token: 0, - amount: new BigNumber(2) - }, - data: evmTx - } - }, - { - src: - { - address: evmScript, - domain: TRANSFER_DOMAIN_TYPE.EVM, - amount: { - token: 0, - amount: new BigNumber(1.5) - }, - data: new Uint8Array([]) - }, - dst: { - address: dvmScript, - domain: TRANSFER_DOMAIN_TYPE.DVM, - amount: { - token: 0, - amount: new BigNumber(1.5) - }, - data: evmTx1 - } - } - ] - } + expect(outs[1].value).toEqual(expect.any(Number)) + expect(outs[1].n).toStrictEqual(1) + expect(outs[1].tokenId).toStrictEqual(0) + expect(outs[1].scriptPubKey.type).toStrictEqual('witness_v0_keyhash') + expect(outs[1].scriptPubKey.addresses[0]).toStrictEqual(dvmAddr) - const txn = await builder.account.transferDomain(transferDomain, dvmScript) - const promise = sendTransaction(testing.container, txn) + const dvmAccAfter = await testing.rpc.account.getAccount(dvmAddr) + const [dvmBalanceAfter0, tokenIdAfter0] = dvmAccAfter[1].split('@') + expect(tokenIdBefore0).toStrictEqual(tokenIdAfter0) - await expect(promise).rejects.toThrow(DeFiDRpcError) - await expect(promise).rejects.toThrow('TransferDomain currently only supports a single transfer per transaction') + // check: dvm balance is transferred + expect(new BigNumber(dvmBalanceAfter0)) + .toStrictEqual(new BigNumber(dvmBalanceBefore0).minus(3)) }) - it('should fail (duo-diff) Transfer Domain from EVM to DVM and DVM to EVM', async () => { - let evmTx = new Uint8Array([]) - { - // EvmIn - const from = evmAddr - const to = evmAddr - const amount = '0x3782dace9d900000' // 4_000_000_000_000_000_000 - const native = dvmAddr - const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + it('should transfer domain dToken from EVM to DVM', async () => { + await waitForExpect(async () => { + const nonce = await rpc.getTransactionCount(evmAddr) + expect(nonce).toStrictEqual(3) + }, 100_000) - const tx: ethers.TransactionRequest = { - to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, - data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee - } - - const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` - - evmTx = new Uint8Array(Buffer.from(signed, 'hex')) - } + const dvmAccBefore = await testing.rpc.account.getAccount(dvmAddr) + const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[1].split('@') - let evmTx1 = new Uint8Array([0]) + let evmTx = new Uint8Array([]) { // EvmOut const from = evmAddr const to = TD_CONTRACT_ADDR const amount = '0x29a2241af62c0000' // 3_000_000_000_000_000_000 const native = dvmAddr - const data = tdFace.encodeFunctionData('transfer', [from, to, amount, native]) + const data = tdFace.encodeFunctionData('transferDST20', [DST_20_CONTRACT_ADDR_BTC, from, to, amount, native]) + const nonce = await rpc.getTransactionCount(evmAddr) const tx: ethers.TransactionRequest = { to: TD_CONTRACT_ADDR, - nonce: await rpc.getTransactionCount(evmAddr), - value: 0, - chainId: (await rpc.getNetwork()).chainId, + nonce: nonce, data: data, - gasLimit: 100_000, - gasPrice: (await rpc.getFeeData()).gasPrice // base fee + chainId: (await rpc.getNetwork()).chainId, + type: 0, + value: 0, + gasLimit: 0, + gasPrice: 0 } const signed = (await wallet.signTransaction(tx)).substring(2) // rm prefix `0x` - evmTx1 = new Uint8Array(Buffer.from(signed, 'hex')) + evmTx = new Uint8Array(Buffer.from(signed, 'hex')) } + const transferDomain: TransferDomain = { items: [{ - src: - { - address: dvmScript, - domain: TRANSFER_DOMAIN_TYPE.DVM, - amount: { - token: 0, - amount: new BigNumber(4) - }, - data: new Uint8Array([]) - }, - dst: { - address: evmScript, - domain: TRANSFER_DOMAIN_TYPE.EVM, - amount: { - token: 0, - amount: new BigNumber(4) - }, - data: evmTx - } - }, - { src: { address: evmScript, domain: TRANSFER_DOMAIN_TYPE.EVM, amount: { - token: 0, + token: 1, amount: new BigNumber(3) }, - data: new Uint8Array([]) + data: evmTx }, dst: { address: dvmScript, domain: TRANSFER_DOMAIN_TYPE.DVM, amount: { - token: 0, + token: 1, amount: new BigNumber(3) }, - data: evmTx1 + data: new Uint8Array([]) } - } - ] + }] } const txn = await builder.account.transferDomain(transferDomain, dvmScript, { maximumAmount: 50 }) - const promise = sendTransaction(testing.container, txn) - await expect(promise).rejects.toThrow(DeFiDRpcError) - await expect(promise).rejects.toThrow('TransferDomain currently only supports a single transfer per transaction') + const outs = await sendTransaction(container, txn) + const encoded: string = OP_CODES.OP_DEFI_TX_TRANSFER_DOMAIN(transferDomain).asBuffer().toString('hex') + const expectedTransferDomainScript = `6a${encoded}` + + expect(outs.length).toStrictEqual(2) + expect(outs[0].value).toStrictEqual(0) + expect(outs[0].n).toStrictEqual(0) + expect(outs[0].tokenId).toStrictEqual(0) + expect(outs[0].scriptPubKey.asm.startsWith('OP_RETURN 4466547838')).toStrictEqual(true) + expect(outs[0].scriptPubKey.hex).toStrictEqual(expectedTransferDomainScript) + expect(outs[0].scriptPubKey.type).toStrictEqual('nulldata') + + expect(outs[1].value).toEqual(expect.any(Number)) + expect(outs[1].n).toStrictEqual(1) + expect(outs[1].tokenId).toStrictEqual(0) + expect(outs[1].scriptPubKey.type).toStrictEqual('witness_v0_keyhash') + expect(outs[1].scriptPubKey.addresses[0]).toStrictEqual(dvmAddr) + + const dvmAccAfter = await testing.rpc.account.getAccount(dvmAddr) + const [dvmBalanceAfter0, tokenIdAfter0] = dvmAccAfter[1].split('@') + expect(tokenIdBefore0).toStrictEqual(tokenIdAfter0) + + // check: dvm balance is updated + expect(new BigNumber(dvmBalanceAfter0)) + .toStrictEqual(new BigNumber(dvmBalanceBefore0).plus(3)) }) }) diff --git a/packages/testcontainers/src/containers/DeFiDContainer.ts b/packages/testcontainers/src/containers/DeFiDContainer.ts index 2779137b0..64ad77997 100644 --- a/packages/testcontainers/src/containers/DeFiDContainer.ts +++ b/packages/testcontainers/src/containers/DeFiDContainer.ts @@ -36,7 +36,7 @@ export abstract class DeFiDContainer extends DockerContainer { if (process?.env?.DEFICHAIN_DOCKER_IMAGE !== undefined) { return process.env.DEFICHAIN_DOCKER_IMAGE } - return 'defi/defichain:master-b352814976' // renovate.json regexManagers + return 'defi/defichain:4.0.0-beta13' // renovate.json regexManagers } public static readonly DefaultStartOptions = { diff --git a/packages/testcontainers/src/containers/NativeChainContainer.ts b/packages/testcontainers/src/containers/NativeChainContainer.ts index cda45f483..9824b97d0 100644 --- a/packages/testcontainers/src/containers/NativeChainContainer.ts +++ b/packages/testcontainers/src/containers/NativeChainContainer.ts @@ -29,7 +29,7 @@ export class NativeChainContainer extends GenericContainer { if (process?.env?.DEFICHAIN_DOCKER_IMAGE !== undefined) { return process.env.DEFICHAIN_DOCKER_IMAGE } - return 'defi/defichain:master-b352814976' // renovate.json regexManagers + return 'defi/defichain:4.0.0-beta13' // renovate.json regexManagers } public static readonly PREFIX = 'defichain-testcontainers-'