ethereum: properly realign transfer decimals, handle decimals on ETH transfers, don't allow same-chain transfers

Co-authored-by: Valentin von Albrecht <valentinvonalbrecht@yahoo.de>
This commit is contained in:
Hendrik Hofstadt 2020-08-30 19:25:31 +02:00
parent 770d396c61
commit 3e88ed023e
2 changed files with 30 additions and 16 deletions

View File

@ -1,8 +1,6 @@
// contracts/Wormhole.sol
// SPDX-License-Identifier: Apache 2
// TODO(hendrik): switch-over feature
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
@ -185,6 +183,12 @@ contract Wormhole is ReentrancyGuard {
} else {
address token_address = data.toAddress(71 + 12);
uint8 decimals = ERC20(token_address).decimals();
// Readjust decimals if they've previously been truncated
if (decimals > 9) {
amount = amount.mul(10 ** uint256(decimals - 9));
}
IERC20(token_address).safeTransfer(target_address, amount);
}
}
@ -217,7 +221,7 @@ contract Wormhole is ReentrancyGuard {
uint32 nonce,
bool refund_dust
) public nonReentrant {
require(amount != 0, "amount must not be 0");
require(target_chain != CHAIN_ID, "must not transfer to the same chain");
uint8 asset_chain = CHAIN_ID;
bytes32 asset_address;
@ -242,7 +246,7 @@ contract Wormhole is ReentrancyGuard {
amount = amount.div(10 ** uint256(decimals - 9));
if (refund_dust) {
ERC20(asset).transfer(msg.sender, original_amount.mod(10 ** uint256(decimals - 9)));
IERC20(asset).safeTransfer(msg.sender, original_amount.mod(10 ** uint256(decimals - 9)));
}
decimals = 9;
@ -253,6 +257,9 @@ contract Wormhole is ReentrancyGuard {
asset_address = bytes32(uint256(asset));
}
// Check here after truncation
require(amount != 0, "truncated amount must not be 0");
emit LogTokensLocked(target_chain, asset_chain, decimals, asset_address, bytes32(uint256(msg.sender)), recipient, amount, nonce);
}
@ -261,13 +268,20 @@ contract Wormhole is ReentrancyGuard {
uint8 target_chain,
uint32 nonce
) public payable nonReentrant {
require(msg.value != 0, "amount must not be 0");
require(target_chain != CHAIN_ID, "must not transfer to the same chain");
uint256 remainder = msg.value.mod(10 ** 9);
uint256 transfer_amount = msg.value.div(10 ** 9);
require(transfer_amount != 0, "truncated amount must not be 0");
// Transfer back remainder
msg.sender.transfer(remainder);
// Wrap tx value in WETH
WETH(WETHAddress).deposit{value : msg.value}();
WETH(WETHAddress).deposit{value : msg.value - remainder}();
// Log deposit of WETH
emit LogTokensLocked(target_chain, CHAIN_ID, 18, bytes32(uint256(WETHAddress)), bytes32(uint256(msg.sender)), recipient, msg.value, nonce);
emit LogTokensLocked(target_chain, CHAIN_ID, 9, bytes32(uint256(WETHAddress)), bytes32(uint256(msg.sender)), recipient, transfer_amount, nonce);
}
fallback() external payable {revert("please use lockETH to transfer ETH to Solana");}

View File

@ -87,7 +87,7 @@ contract("Wormhole", function () {
// Expect user to have a balance
let wa = new WrappedAsset("0xC3697aaf5B3D354214548248710414812099bc93")
await bridge.lockAssets(wa.address, "500000000000000000", "0x0", 2, 2, false);
await bridge.lockAssets(wa.address, "500000000000000000", "0x0", 4, 2, false);
let balance = await wa.balanceOf("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1");
// Expect user balance to decrease
@ -102,12 +102,12 @@ contract("Wormhole", function () {
let bridge = await Wormhole.deployed();
let token = await ERC20.new("Test Token", "TKN");
await token.mint("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1", "1000000000000000000");
await token.mint("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1", "1000000000000000000000000000");
// Expect user to have a balance
assert.equal(await token.balanceOf("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"), "1000000000000000000");
assert.equal(await token.balanceOf("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"), "1000000000000000000000000000");
// Approve bridge
await token.approve(bridge.address, "1000000000000000000");
await token.approve(bridge.address, "1000000000000000000000000000");
// Transfer of that token out of the contract should not work
let threw = false;
@ -119,7 +119,7 @@ contract("Wormhole", function () {
assert.isTrue(threw);
// Lock assets
let ev = await bridge.lockAssets(token.address, "1000000000000000000", "0x1230000000000000000000000000000000000000000000000000000000000000", 3, 3, false);
let ev = await bridge.lockAssets(token.address, "1000000000000000000000000000", "0x1230000000000000000000000000000000000000000000000000000000000000", 3, 3, false);
// Check that the lock event was emitted correctly
assert.lengthOf(ev.logs, 1)
@ -129,15 +129,15 @@ contract("Wormhole", function () {
assert.equal(ev.logs[0].args.token, "0x000000000000000000000000d833215cbcc3f914bd1c9ece3ee7bf8b14f841bb")
assert.equal(ev.logs[0].args.sender, "0x00000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1")
assert.equal(ev.logs[0].args.recipient, "0x1230000000000000000000000000000000000000000000000000000000000000")
assert.equal(ev.logs[0].args.amount, "1000000000")
assert.equal(ev.logs[0].args.amount, "1000000000000000000")
// Check that the tokens were transferred to the bridge
assert.equal(await token.balanceOf("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"), "0");
assert.equal(await token.balanceOf(bridge.address), "1000000000000000000");
assert.equal(await token.balanceOf(bridge.address), "1000000000000000000000000000");
// Transfer this token back
// Transfer this token back - This also checks decimal realignment
await bridge.submitVAA("0x01000000000100078f0fe9406808b1e5003867ab74aa2085153b7735b329640d275ea943dd115d00e356c6d343142d9190872c11d2de898d075cea7f4e85ff2188af299e26a14200000007d010000000380102020104000000000000000000000000000000000000000000000000000000000000000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c102000000000000000000000000d833215cbcc3f914bd1c9ece3ee7bf8b14f841bb080000000000000000000000000000000000000000000000000de0b6b3a7640000");
assert.equal(await token.balanceOf("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"), "1000000000000000000");
assert.equal(await token.balanceOf("0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"), "1000000000000000000000000000");
assert.equal(await token.balanceOf(bridge.address), "0");
});