Skip to content

Commit

Permalink
Add nonce verification for deposits
Browse files Browse the repository at this point in the history
  • Loading branch information
kiseln committed Sep 4, 2024
1 parent 8aaa89d commit e86a9ae
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 0 deletions.
9 changes: 9 additions & 0 deletions erc20-bridge-token/contracts/BridgeTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ contract BridgeTokenFactory is
mapping(bytes => bool) private _whitelistedAccounts;
bool private _isWhitelistModeEnabled;

mapping(uint128 => bool) private _completedTransfers;

address public tokenImplementationAddress;
address public nearBridgeDerivedAddress;

Expand Down Expand Up @@ -73,6 +75,7 @@ contract BridgeTokenFactory is
);

error InvalidSignature();
error NonceAlreadyUsed();

// BridgeTokenFactory is linked to the bridge token factory on NEAR side.
// It also links to the prover that it uses to unlock the tokens.
Expand Down Expand Up @@ -167,6 +170,10 @@ contract BridgeTokenFactory is
}

function deposit(bytes calldata signatureData, BridgeDeposit calldata bridgeDeposit) external whenNotPaused(PAUSED_DEPOSIT) {
if (_completedTransfers[bridgeDeposit.nonce]) {
revert NonceAlreadyUsed();
}

bytes memory borshEncoded = bytes.concat(
Borsh.encodeUint128(bridgeDeposit.nonce),
Borsh.encodeString(bridgeDeposit.token),
Expand All @@ -186,6 +193,8 @@ contract BridgeTokenFactory is
require(_isBridgeToken[_nearToEthToken[bridgeDeposit.token]], "ERR_NOT_BRIDGE_TOKEN");
BridgeToken(_nearToEthToken[bridgeDeposit.token]).mint(bridgeDeposit.recipient, bridgeDeposit.amount);

_completedTransfers[bridgeDeposit.nonce] = true;

emit Deposit(bridgeDeposit.token, bridgeDeposit.amount, bridgeDeposit.recipient);
}

Expand Down
72 changes: 72 additions & 0 deletions erc20-bridge-token/test/BridgeToken.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,78 @@ describe('BridgeToken', () => {
.revertedWith('Pausable: paused');
})

it('can\'t deposit twice with the same signature', async function () {
await createToken(wrappedNearId);

const { signature, payload } = depositSignature(wrappedNearId, user1.address);
await BridgeTokenFactory.deposit(signature, payload);

await expect(
BridgeTokenFactory.deposit(signature, payload)
)
.to.be.revertedWithCustomError(BridgeTokenFactory, 'NonceAlreadyUsed');
})

it('can\'t deposit with invalid amount', async function () {
await createToken(wrappedNearId);

const { signature, payload } = depositSignature(wrappedNearId, user1.address);
payload.amount = 100000;

await expect(
BridgeTokenFactory.deposit(signature, payload)
)
.to.be.revertedWithCustomError(BridgeTokenFactory, 'InvalidSignature');
})

it('can\'t deposit with invalid nonce', async function () {
await createToken(wrappedNearId);

const { signature, payload } = depositSignature(wrappedNearId, user1.address);
payload.nonce = 99;

await expect(
BridgeTokenFactory.deposit(signature, payload)
)
.to.be.revertedWithCustomError(BridgeTokenFactory, 'InvalidSignature');
})

it('can\'t deposit with invalid token', async function () {
await createToken(wrappedNearId);

const { signature, payload } = depositSignature(wrappedNearId, user1.address);
payload.token = 'test-token.testnet';

await expect(
BridgeTokenFactory.deposit(signature, payload)
)
.to.be.revertedWithCustomError(BridgeTokenFactory, 'InvalidSignature');
})

it('can\'t deposit with invalid recipient', async function () {
await createToken(wrappedNearId);

const { signature, payload } = depositSignature(wrappedNearId, user1.address);
payload.recipient = user2.address;

await expect(
BridgeTokenFactory.deposit(signature, payload)
)
.to.be.revertedWithCustomError(BridgeTokenFactory, 'InvalidSignature');
})

it('can\'t deposit with invalid relayer', async function () {
await createToken(wrappedNearId);

const { signature, payload } = depositSignature(wrappedNearId, user1.address);
payload.relayer = user2.address;

await expect(
BridgeTokenFactory.deposit(signature, payload)
)
.to.be.revertedWithCustomError(BridgeTokenFactory, 'InvalidSignature');
})

it('withdraw token', async function () {
const { token } = await createToken(wrappedNearId);

Expand Down

0 comments on commit e86a9ae

Please # to comment.