Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"turbo": "^1.10.7"
},
"devDependencies": {
"@changesets/cli": "^2.26.2",
"@changesets/cli": "^2.27.11",
"@fuel-ts/forc": "0.73.0"
},
"pnpm": {
Expand Down
169 changes: 150 additions & 19 deletions packages/integration-tests/fork-tests/bridge_erc20.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
getBlock,
FUEL_CALL_TX_PARAMS,
hardhatSkipTime,
fuels_parseEther,
} from '@fuel-bridge/test-utils';
import chai from 'chai';
import { toBeHex, parseEther } from 'ethers';
Expand All @@ -28,8 +29,10 @@ import { Address, BN } from 'fuels';
import type {
AbstractAddress,
WalletUnlocked as FuelWallet,
MessageCoin,
MessageProof,
Provider,
ScriptTransactionRequest,
} from 'fuels';

const { expect } = chai;
Expand All @@ -56,12 +59,103 @@ describe('Bridging ERC20 tokens', async function () {
await provider.produceBlocks(Number(blocksToForward)).catch(console.error);
}

async function fetchTransaction(
fuel_bridge: BridgeFungibleToken,
fuelTokenSender: FuelWallet,
to: string,
NUM_TOKENS: bigint,
DECIMAL_DIFF: bigint,
useMessageCoin: boolean
): Promise<ScriptTransactionRequest> {
if (useMessageCoin) {
const tx = await fuel_bridge.functions
.withdraw(to)
.addContracts([fuel_bridge, fuel_bridgeImpl])
.txParams({
tip: 0,
maxFee: 1,
})
.callParams({
forward: {
amount: new BN(NUM_TOKENS.toString()).div(
new BN(DECIMAL_DIFF.toString())
),
assetId: fuel_testAssetId,
},
});

// verify the incoming messages generated when base asset is minted on fuel
let incomingMessagesonFuel = await env.fuel.signers[0].getMessages();

expect(incomingMessagesonFuel.messages.length === 1);
expect(
incomingMessagesonFuel.messages[0].amount === fuels_parseEther('1')
);

// construct message coin
const messageCoin: MessageCoin = {
assetId: env.fuel.provider.getBaseAssetId(),
sender: incomingMessagesonFuel.messages[0].sender,
recipient: incomingMessagesonFuel.messages[0].recipient,
nonce: incomingMessagesonFuel.messages[0].nonce,
daHeight: incomingMessagesonFuel.messages[0].daHeight,
amount: incomingMessagesonFuel.messages[0].amount,
};

const transactionRequest = await tx.getTransactionRequest();

// add message coin as input to fund the tx
transactionRequest.addMessageInput(messageCoin);

// add the erc20 token input which will be burnt on withdrawal
const resource = await fuelTokenSender.getResourcesToSpend([
[
new BN(NUM_TOKENS.toString()).div(new BN(DECIMAL_DIFF.toString())),
fuel_testAssetId,
],
]);

transactionRequest.addResources(resource);

// fetch tx cost
const cost = await fuelTokenSender.getTransactionCost(transactionRequest);

// update fee params
transactionRequest.gasLimit = cost.gasUsed;
transactionRequest.maxFee = cost.maxFee;

// verify that the message coin is consumed
incomingMessagesonFuel = await fuelTokenSender.getMessages();
expect(incomingMessagesonFuel.messages.length === 0);

return transactionRequest;
} else {
return await fuel_bridge.functions
.withdraw(to)
.addContracts([fuel_bridge, fuel_bridgeImpl])
.txParams({
tip: 0,
maxFee: 1,
})
.callParams({
forward: {
amount: new BN(NUM_TOKENS.toString()).div(
new BN(DECIMAL_DIFF.toString())
),
assetId: fuel_testAssetId,
},
})
.fundWithRequiredCoins();
}
}

async function generateWithdrawalMessageProof(
fuel_bridge: BridgeFungibleToken,
fuelTokenSender: FuelWallet,
ethereumTokenReceiverAddress: string,
NUM_TOKENS: bigint,
DECIMAL_DIFF: bigint
DECIMAL_DIFF: bigint,
useMessageCoin: boolean
): Promise<MessageProof | null> {
// withdraw tokens back to the base chain
fuel_bridge.account = fuelTokenSender;
Expand All @@ -70,22 +164,15 @@ describe('Bridging ERC20 tokens', async function () {
const fuelTokenSenderBalance = await fuelTokenSender.getBalance(
fuel_testAssetId
);
const transactionRequest = await fuel_bridge.functions
.withdraw(paddedAddress)
.addContracts([fuel_bridge, fuel_bridgeImpl])
.txParams({
tip: 0,
maxFee: 1,
})
.callParams({
forward: {
amount: new BN(NUM_TOKENS.toString()).div(
new BN(DECIMAL_DIFF.toString())
),
assetId: fuel_testAssetId,
},
})
.fundWithRequiredCoins();

const transactionRequest = await fetchTransaction(
fuel_bridge,
fuelTokenSender,
paddedAddress,
NUM_TOKENS,
DECIMAL_DIFF,
useMessageCoin
);

const tx = await fuelTokenSender.sendTransaction(transactionRequest);
const fWithdrawTxResult = await tx.waitForResult();
Expand Down Expand Up @@ -296,6 +383,48 @@ describe('Bridging ERC20 tokens', async function () {
);
});

it('Bridge ETH to Fuel to be used as Message Coin during token withdrawal', async () => {
// use the FuelMessagePortal to directly send ETH which should be immediately spendable
const tx = await env.eth.fuelMessagePortal
.connect(ethereumTokenSender)
.depositETH(fuelTokenReceiverAddress, {
value: parseEther('1'),
});
const receipt = await tx.wait();
expect(receipt.status).to.equal(1);

// parse events from logs
const filter = env.eth.fuelMessagePortal.filters.MessageSent(
null, // Args set to null since there should be just 1 event for MessageSent
null,
null,
null,
null
);

const [event, ...restOfEvents] =
await env.eth.fuelMessagePortal.queryFilter(
filter,
receipt.blockNumber,
receipt.blockNumber
);
expect(restOfEvents.length).to.be.eq(0); // Should be only 1 event

const fuelETHMessageNonce = new BN(event.args.nonce.toString());

fuelTokenMessageReceiver = fuelTokenReceiver.address;

// wait for message to appear in fuel client
expect(
await waitForMessage(
env.fuel.provider,
fuelTokenMessageReceiver,
fuelETHMessageNonce,
FUEL_MESSAGE_TIMEOUT_MS
)
).to.not.be.null;
});

it('Bridge ERC20 via FuelERC20Gateway', async () => {
// approve FuelERC20Gateway to spend the tokens
await eth_testToken
Expand Down Expand Up @@ -455,7 +584,8 @@ describe('Bridging ERC20 tokens', async function () {
fuelTokenSender,
ethereumTokenReceiverAddress,
NUM_TOKENS,
DECIMAL_DIFF
DECIMAL_DIFF,
true
);
});

Expand Down Expand Up @@ -573,7 +703,8 @@ describe('Bridging ERC20 tokens', async function () {
fuelTokenSender,
ethereumTokenReceiverAddress,
NUM_TOKENS,
DECIMAL_DIFF
DECIMAL_DIFF,
false
);

// relay message
Expand Down
Loading
Loading