-
-
Published
Linked with GitHub
# ASE with bridge-contracts
[toc]
## Motivation
This is an alternative proposal building on top of the [Address Space Extension with Translation Map](https://notes.ethereum.org/@ipsilon/address-space-extension-exploration) idea. See [this for open questions](https://notes.ethereum.org/@ipsilon/address-space-extension-issues).
One of the major issues identified is the inability to distinguish short (legacy) addresses from compressed addresses, and the resulting complexity of automatic translation.
In order to avoid this problem, we introduce a special contract type, called bridge contract, which announces itself being capable of interacting with the new address space, but is actually placed in the legacy address space (and has a short address). Other contracts in the legacy space can not interact with the new address space.
## Specification
Assume the address format and execution logic (bar translation map) from the previous write ups:
- address taking/returning instructions operate on 20-byte addresses in contracts in the legacy address space, while on 32-byte addresses in the new address space,
- calling a legacy account from a new address results in an exceptional abort (like the `invalid` instruction), unless it is a bridge contract (see next).
### Bridge contract
A bridge contract is a contract which has a legacy short address, but is marked as capable of understanding long addresses. Legacy accounts do not have such a marker and are unaffected.
There are two options depending what marker do we want:
#### Option 1: EOF / EIP-3540
This option is simple: any contract having a valid [EIP-3540](https://eips.ethereum.org/EIPS/eip-3540) prefix is considered valid as a bridge contract.
*Should there be some risk, a special section/version requirement can be introduced to restrict the number of contracts.*
#### Option 2: Special creation
Introduce a new field in the account, `is_bridge`, which if true announces a contract to be a bridge contract.
Such an account can be created via, either:
a) A new transaction type specficially for this
b) A new CREATE-instruction specifically for this
### Instructions
The instructions for the bridge contract context operate like they do in the new address space: they take and return 32-byte addresses.
When a bridge contract calls another contract, it will be visible with a legacy short address. This makes it possible for them to interact with legacy contracts without any workarounds.
With this in place, various wrappers or bridges can be written to move (token) balances between the different address spaces, or to even proxy calls between them.
### Translation map
There is no need for a translation map.
## Examples
### Simple token visible on both sides
It is possible to deploy a new token as a bridge contract, allowing to hold balances for both legacy and new account, **and** taking transfers from legacy accounts.
Since `address` and `address32` would be distinct types for both the ABI encoding and Solidity, it would be possible to write a single contract which has both ERC-20-like interfaces:
```solidity
interface CompatibleToken {
// Legacy interface
function balanceOf(address recipient) external view returns (uint);
function transfer(address recipient, uint amount) external;
// Modern interface, accepting both address-types
function balanceOf(address32 recipient) external view returns (uint);
function transfer(address32 recipient, uint amount) external;
// ... the rest of the functions ...
}
```
We need two entry points, as the selectors for the dispatch table are generated based on function name and argument types.
### Simple token bridge
This example is a contract which burns tokens on the legacy space, and mints new ones on the new space. The benefit is that the tokens are moved to the new address space.
```solidity
contract Bridge {
// The traditional ERC-20 standard with only address
IERC20 oldToken;
// ERC-NEWT is the new token standard supporting address32
IERCNEWT newToken;
constructor(address _oldToken, address32 _newToken) {
oldToken = IERC20(_oldToken);
newToken = IERCNEWT(_newToken);
}
function swap(address32 to, uint amount) {
// Ensure we are being called from within the legacy space
require(uint256(msg.sender) <= 2**160-1);
oldToken.transferFrom(msg.sender, address(this), amount);
// Asuming oldToken allows for self burning
oldToken.burn(amount);
// Assuming the bridge is a minter
newToken.mint(to, amount);
}
}
```
#### Use case: Moving legacy DAI to new space
Execute two transactions in the legacy context:
1. Call `DAI.approve(bridge, 100)`
2. Call `Bridge.swap(recipient_long_address, 100)`
### Generic proxy
A generic proxy forwarder contract (as available in OpenZeppelin) should just work out of the box.
## Questions
- Could keep a translation map too, in order to inspect/use it within bridge contracts -- any usefulness?
- ?