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:
parent
6648e49807
commit
745ff0ba9b
|
@ -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);
|
||||
|
|
|
@ -65,5 +65,6 @@ contract BridgeGetters is BridgeState {
|
|||
|
||||
interface IWETH is IERC20 {
|
||||
function deposit() external payable;
|
||||
|
||||
function withdraw(uint amount) external;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
{}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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");
|
||||
_;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue