Allow to update meta on ETH, new token naming

This allows us to update the token name and symbol once solana adds the appropriate metadata.

Change-Id: Ibcabf2a644bcc2b2962ff779fa7fb3cb1f2cc626
This commit is contained in:
Hendrik Hofstadt 2021-09-01 13:13:51 +02:00
parent 6648e49807
commit 745ff0ba9b
7 changed files with 95 additions and 58 deletions

View File

@ -34,29 +34,29 @@ contract Bridge is BridgeGovernance {
bytes32 symbol;
bytes32 name;
assembly {
// first 32 bytes hold string length
// first 32 bytes hold string length
symbol := mload(add(symbolString, 32))
name := mload(add(nameString, 32))
}
BridgeStructs.AssetMeta memory meta = BridgeStructs.AssetMeta({
payloadID : 2,
// Address of the token. Left-zero-padded if shorter than 32 bytes
tokenAddress : bytes32(uint256(uint160(tokenAddress))),
// Chain ID of the token
tokenChain : chainId(),
// Number of decimals of the token (big-endian uint8)
decimals : decimals,
// Symbol of the token (UTF-8)
symbol : symbol,
// Name of the token (UTF-8)
name : name
payloadID : 2,
// Address of the token. Left-zero-padded if shorter than 32 bytes
tokenAddress : bytes32(uint256(uint160(tokenAddress))),
// Chain ID of the token
tokenChain : chainId(),
// Number of decimals of the token (big-endian uint8)
decimals : decimals,
// Symbol of the token (UTF-8)
symbol : symbol,
// Name of the token (UTF-8)
name : name
});
bytes memory encoded = encodeAssetMeta(meta);
sequence = wormhole().publishMessage{
value : msg.value
value : msg.value
}(nonce, encoded, 15);
}
@ -69,18 +69,18 @@ contract Bridge is BridgeGovernance {
require(arbiterFee <= amount, "fee is bigger than amount minus wormhole fee");
uint normalizedAmount = amount / (10**10);
uint normalizedArbiterFee = arbiterFee / (10**10);
uint normalizedAmount = amount / (10 ** 10);
uint normalizedArbiterFee = arbiterFee / (10 ** 10);
// refund dust
uint dust = amount - (normalizedAmount * (10**10));
uint dust = amount - (normalizedAmount * (10 ** 10));
if (dust > 0) {
payable(msg.sender).transfer(dust);
payable(msg.sender).transfer(dust);
}
// deposit into WETH
WETH().deposit{
value : amount - dust
value : amount - dust
}();
// track and check outstanding token amounts
@ -94,10 +94,10 @@ contract Bridge is BridgeGovernance {
// determine token parameters
uint16 tokenChain;
bytes32 tokenAddress;
if(isWrappedAsset(token)){
if (isWrappedAsset(token)) {
tokenChain = TokenImplementation(token).chainId();
tokenAddress = TokenImplementation(token).nativeContract();
}else{
} else {
tokenChain = chainId();
tokenAddress = bytes32(uint256(uint160(token)));
}
@ -109,8 +109,8 @@ contract Bridge is BridgeGovernance {
// adjust decimals
uint256 normalizedAmount = amount;
uint256 normalizedArbiterFee = arbiterFee;
if(decimals > 8) {
uint multiplier = 10**(decimals - 8);
if (decimals > 8) {
uint multiplier = 10 ** (decimals - 8);
normalizedAmount /= multiplier;
normalizedArbiterFee /= multiplier;
@ -119,7 +119,7 @@ contract Bridge is BridgeGovernance {
amount = normalizedAmount * multiplier;
}
if(tokenChain == chainId()){
if (tokenChain == chainId()) {
SafeERC20.safeTransferFrom(IERC20(token), msg.sender, address(this), amount);
// track and check outstanding token amounts
@ -137,22 +137,42 @@ contract Bridge is BridgeGovernance {
require(fee <= amount, "fee exceeds amount");
BridgeStructs.Transfer memory transfer = BridgeStructs.Transfer({
payloadID : 1,
amount : amount,
tokenAddress : tokenAddress,
tokenChain : tokenChain,
to : recipient,
toChain : recipientChain,
fee : fee
payloadID : 1,
amount : amount,
tokenAddress : tokenAddress,
tokenChain : tokenChain,
to : recipient,
toChain : recipientChain,
fee : fee
});
bytes memory encoded = encodeTransfer(transfer);
sequence = wormhole().publishMessage{
value : callValue
value : callValue
}(nonce, encoded, 15);
}
function updateWrapped(bytes memory encodedVm) external returns (address token) {
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole().parseAndVerifyVM(encodedVm);
require(valid, reason);
require(verifyBridgeVM(vm), "invalid emitter");
BridgeStructs.AssetMeta memory meta = parseAssetMeta(vm.payload);
return _updateWrapped(meta, vm.sequence);
}
function _updateWrapped(BridgeStructs.AssetMeta memory meta, uint64 sequence) internal returns (address token) {
address wrapped = wrappedAsset(meta.tokenChain, meta.tokenAddress);
require(wrapped != address(0), "wrapped asset does not exists");
// Update metadata
TokenImplementation(wrapped).updateDetails(bytes32ToString(meta.name), bytes32ToString(meta.symbol), sequence);
return wrapped;
}
function createWrapped(bytes memory encodedVm) external returns (address token) {
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole().parseAndVerifyVM(encodedVm);
@ -160,11 +180,11 @@ contract Bridge is BridgeGovernance {
require(verifyBridgeVM(vm), "invalid emitter");
BridgeStructs.AssetMeta memory meta = parseAssetMeta(vm.payload);
return _createWrapped(meta);
return _createWrapped(meta, vm.sequence);
}
// Creates a wrapped asset using AssetMeta
function _createWrapped(BridgeStructs.AssetMeta memory meta) internal returns (address token) {
// Creates a wrapped asset using AssetMeta
function _createWrapped(BridgeStructs.AssetMeta memory meta, uint64 sequence) internal returns (address token) {
require(meta.tokenChain != chainId(), "can only wrap tokens from foreign chains");
require(wrappedAsset(meta.tokenChain, meta.tokenAddress) == address(0), "wrapped asset already exists");
@ -174,6 +194,7 @@ contract Bridge is BridgeGovernance {
bytes32ToString(meta.name),
bytes32ToString(meta.symbol),
meta.decimals,
sequence,
address(this),
@ -223,7 +244,7 @@ contract Bridge is BridgeGovernance {
require(transfer.toChain == chainId(), "invalid target chain");
IERC20 transferToken;
if(transfer.tokenChain == chainId()){
if (transfer.tokenChain == chainId()) {
transferToken = IERC20(address(uint160(uint256(transfer.tokenAddress))));
// track outstanding token amounts
@ -244,14 +265,14 @@ contract Bridge is BridgeGovernance {
// adjust decimals
uint256 nativeAmount = transfer.amount;
uint256 nativeFee = transfer.fee;
if(decimals > 8) {
uint multiplier = 10**(decimals - 8);
if (decimals > 8) {
uint multiplier = 10 ** (decimals - 8);
nativeAmount *= multiplier;
nativeFee *= multiplier;
}
// transfer fee to arbiter
if(nativeFee > 0) {
if (nativeFee > 0) {
require(nativeFee <= nativeAmount, "fee higher than transferred amount");
if (unwrapWETH) {
@ -259,10 +280,10 @@ contract Bridge is BridgeGovernance {
payable(msg.sender).transfer(nativeFee);
} else {
if(transfer.tokenChain != chainId()) {
if (transfer.tokenChain != chainId()) {
// mint wrapped asset
TokenImplementation(address(transferToken)).mint(msg.sender, nativeFee);
}else{
} else {
SafeERC20.safeTransfer(transferToken, msg.sender, nativeFee);
}
}
@ -277,10 +298,10 @@ contract Bridge is BridgeGovernance {
payable(transferRecipient).transfer(transferAmount);
} else {
if(transfer.tokenChain != chainId()) {
if (transfer.tokenChain != chainId()) {
// mint wrapped asset
TokenImplementation(address(transferToken)).mint(transferRecipient, transferAmount);
}else{
} else {
SafeERC20.safeTransfer(transferToken, transferRecipient, transferAmount);
}
}
@ -304,7 +325,7 @@ contract Bridge is BridgeGovernance {
return false;
}
function encodeAssetMeta(BridgeStructs.AssetMeta memory meta) public pure returns(bytes memory encoded) {
function encodeAssetMeta(BridgeStructs.AssetMeta memory meta) public pure returns (bytes memory encoded) {
encoded = abi.encodePacked(
meta.payloadID,
meta.tokenAddress,
@ -327,7 +348,7 @@ contract Bridge is BridgeGovernance {
);
}
function parseAssetMeta(bytes memory encoded) public pure returns(BridgeStructs.AssetMeta memory meta) {
function parseAssetMeta(bytes memory encoded) public pure returns (BridgeStructs.AssetMeta memory meta) {
uint index = 0;
meta.payloadID = encoded.toUint8(index);
@ -353,7 +374,7 @@ contract Bridge is BridgeGovernance {
require(encoded.length == index, "invalid AssetMeta");
}
function parseTransfer(bytes memory encoded) public pure returns(BridgeStructs.Transfer memory transfer) {
function parseTransfer(bytes memory encoded) public pure returns (BridgeStructs.Transfer memory transfer) {
uint index = 0;
transfer.payloadID = encoded.toUint8(index);
@ -384,7 +405,7 @@ contract Bridge is BridgeGovernance {
function bytes32ToString(bytes32 input) internal pure returns (string memory) {
uint256 i;
while(i < 32 && input[i] != 0) {
while (i < 32 && input[i] != 0) {
i++;
}
bytes memory array = new bytes(i);

View File

@ -65,5 +65,6 @@ contract BridgeGetters is BridgeState {
interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint amount) external;
}

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
@ -54,7 +55,7 @@ contract BridgeGovernance is BridgeGetters, BridgeSetters, ERC1967Upgrade {
function verifyGovernanceVM(bytes memory encodedVM) internal view returns (IWormhole.VM memory parsedVM, bool isValid, string memory invalidReason){
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole().parseAndVerifyVM(encodedVM);
if(!valid){
if (!valid) {
return (vm, valid, reason);
}
@ -65,7 +66,7 @@ contract BridgeGovernance is BridgeGetters, BridgeSetters, ERC1967Upgrade {
return (vm, false, "wrong governance contract");
}
if(governanceActionIsConsumed(vm.hash)){
if (governanceActionIsConsumed(vm.hash)) {
return (vm, false, "governance action already consumed");
}
@ -73,6 +74,7 @@ contract BridgeGovernance is BridgeGetters, BridgeSetters, ERC1967Upgrade {
}
event ContractUpgraded(address indexed oldContract, address indexed newContract);
function upgradeImplementation(address newImplementation) internal {
address currentImplementation = _getImplementation();
@ -86,7 +88,7 @@ contract BridgeGovernance is BridgeGetters, BridgeSetters, ERC1967Upgrade {
emit ContractUpgraded(currentImplementation, newImplementation);
}
function parseRegisterChain(bytes memory encoded) public pure returns(BridgeStructs.RegisterChain memory chain) {
function parseRegisterChain(bytes memory encoded) public pure returns (BridgeStructs.RegisterChain memory chain) {
uint index = 0;
// governance header
@ -113,7 +115,7 @@ contract BridgeGovernance is BridgeGetters, BridgeSetters, ERC1967Upgrade {
require(encoded.length == index, "invalid RegisterChain: wrong length");
}
function parseUpgrade(bytes memory encoded) public pure returns(BridgeStructs.UpgradeContract memory chain) {
function parseUpgrade(bytes memory encoded) public pure returns (BridgeStructs.UpgradeContract memory chain) {
uint index = 0;
// governance header

View File

@ -7,9 +7,9 @@ import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
contract TokenBridge is ERC1967Proxy {
constructor (address implementation, bytes memory initData)
ERC1967Proxy(
implementation,
initData
)
ERC1967Proxy(
implementation,
initData
)
{}
}

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
contract BridgeToken is BeaconProxy {

View File

@ -17,6 +17,7 @@ contract TokenImplementation is TokenState, Context {
string memory name_,
string memory symbol_,
uint8 decimals_,
uint64 sequence_,
address owner_,
@ -26,6 +27,7 @@ contract TokenImplementation is TokenState, Context {
_state.name = name_;
_state.symbol = symbol_;
_state.decimals = decimals_;
_state.metaLastUpdatedSequence = sequence_;
_state.owner = owner_;
@ -34,11 +36,11 @@ contract TokenImplementation is TokenState, Context {
}
function name() public view returns (string memory) {
return string(abi.encodePacked("Wormhole: ", _state.name));
return string(abi.encodePacked(_state.name, " (Wormhole)"));
}
function symbol() public view returns (string memory) {
return string(abi.encodePacked("wh", _state.symbol));
return _state.symbol;
}
function owner() public view returns (address) {
@ -149,6 +151,14 @@ contract TokenImplementation is TokenState, Context {
emit Approval(owner_, spender_, amount_);
}
function updateDetails(string memory name_, string memory symbol_, uint64 sequence_) public onlyOwner {
require(_state.metaLastUpdatedSequence < sequence_, "current metadata is up to date");
_state.name = name_;
_state.symbol = symbol_;
_state.metaLastUpdatedSequence = sequence_;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "caller is not the owner");
_;

View File

@ -8,12 +8,14 @@ contract TokenStorage {
string name;
string symbol;
uint64 metaLastUpdatedSequence;
uint256 totalSupply;
uint8 decimals;
mapping (address => uint256) balances;
mapping(address => uint256) balances;
mapping (address => mapping (address => uint256)) allowances;
mapping(address => mapping(address => uint256)) allowances;
address owner;