This repository has been archived by the owner on Oct 22, 2024. It is now read-only.
forked from paritytech/polkadot-sdk
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Relayer Rewards + Wrapped DOT (paritytech#271)
- Loading branch information
Showing
33 changed files
with
1,696 additions
and
119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity >=0.7.6; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "./WrappedToken.sol"; | ||
import "./ScaleCodec.sol"; | ||
import "./OutboundChannel.sol"; | ||
|
||
enum ChannelId {Basic, Incentivized} | ||
|
||
abstract contract BaseDOTApp { | ||
using ScaleCodec for uint128; | ||
|
||
mapping(ChannelId => Channel) public channels; | ||
|
||
bytes2 constant UNLOCK_CALL = 0x0e01; | ||
|
||
WrappedToken public token; | ||
|
||
struct Channel { | ||
address inbound; | ||
address outbound; | ||
} | ||
|
||
constructor( | ||
string memory _name, | ||
string memory _symbol, | ||
Channel memory _basic, | ||
Channel memory _incentivized | ||
) { | ||
address[] memory defaultOperators; | ||
token = new WrappedToken(_name, _symbol, defaultOperators); | ||
|
||
Channel storage c1 = channels[ChannelId.Basic]; | ||
c1.inbound = _basic.inbound; | ||
c1.outbound = _basic.outbound; | ||
|
||
Channel storage c2 = channels[ChannelId.Incentivized]; | ||
c2.inbound = _incentivized.inbound; | ||
c2.outbound = _incentivized.outbound; | ||
} | ||
|
||
function burn(bytes32 _recipient, uint256 _amount, ChannelId _channelId) public { | ||
require( | ||
_channelId == ChannelId.Basic || | ||
_channelId == ChannelId.Incentivized, | ||
"Invalid channel ID" | ||
); | ||
require(_amount % granularity() == 0, "Invalid Granularity"); | ||
|
||
token.burn(msg.sender, _amount, abi.encodePacked(_recipient)); | ||
|
||
OutboundChannel channel = OutboundChannel(channels[_channelId].outbound); | ||
|
||
bytes memory call = encodeCall(msg.sender, _recipient, unwrap(_amount)); | ||
channel.submit(call); | ||
} | ||
|
||
function mint(bytes32 _sender, address _recipient, uint128 _amount) public { | ||
// TODO: Ensure message sender is a known inbound channel | ||
token.mint(_recipient, wrap(_amount), abi.encodePacked(_sender)); | ||
} | ||
|
||
function encodeCall(address _sender, bytes32 _recipient, uint128 _amount) | ||
private | ||
pure | ||
returns (bytes memory) | ||
{ | ||
return | ||
abi.encodePacked( | ||
UNLOCK_CALL, | ||
_sender, | ||
byte(0x00), // Encoding recipient as MultiAddress::Id | ||
_recipient, | ||
_amount.encode128() | ||
); | ||
} | ||
|
||
/* | ||
* Convert native DOT/KSM/ROC to the wrapped equivalent. | ||
* | ||
* SAFETY: No need for SafeMath.mul since its impossible to overflow | ||
* when 0 <= granularity <= 10 ^ 8, as specified by DOTAppDecimals10.sol | ||
* and DOTAppDecimals12.sol. | ||
* | ||
* Can verify in Rust using this snippet: | ||
* | ||
* let granularity = U256::from(100000000u64); | ||
* U256::from(u128::MAX).checked_mul(granularity).unwrap(); | ||
* | ||
*/ | ||
function wrap(uint128 _value) pure internal returns (uint256) { | ||
return uint256(_value) * granularity(); | ||
} | ||
|
||
/* | ||
* Convert wrapped DOT/KSM/ROC to its native equivalent. | ||
* | ||
* SAFETY: No need for SafeMath.div since granularity() resolves to a non-zero | ||
* constant (See DOTAppDecimals10.sol and DOTAppDecimals12.sol) | ||
*/ | ||
function unwrap(uint256 _value) pure internal returns (uint128) { | ||
return uint128(_value / granularity()); | ||
} | ||
|
||
/** | ||
* Smallest part of DOT/KSM/ROC that is not divisible when increasing | ||
* precision to 18 decimal places. | ||
* | ||
* This is used for converting between native and wrapped | ||
* representations of DOT/KSM/ROC. | ||
*/ | ||
function granularity() pure internal virtual returns (uint256); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity >=0.7.6; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "./BaseDOTApp.sol"; | ||
|
||
contract DOTAppDecimals10 is BaseDOTApp { | ||
// Polkadot (DOT) has 10 decimal places | ||
uint256 constant internal DECIMALS = 10; | ||
uint256 constant internal GRANULARITY = 10 ** (18 - DECIMALS); | ||
|
||
constructor( | ||
string memory _name, | ||
string memory _symbol, | ||
Channel memory _basic, | ||
Channel memory _incentivized | ||
) | ||
BaseDOTApp(_name, _symbol, _basic, _incentivized) | ||
{ } | ||
|
||
function granularity() pure internal override returns (uint256) { | ||
return GRANULARITY; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity >=0.7.6; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "./BaseDOTApp.sol"; | ||
|
||
contract DOTAppDecimals12 is BaseDOTApp { | ||
// Kusama (KSM) and Rococo (ROC) have 12 decimal places | ||
uint256 constant internal DECIMALS = 12; | ||
uint256 constant internal GRANULARITY = 10 ** (18 - DECIMALS); | ||
|
||
constructor( | ||
string memory _name, | ||
string memory _symbol, | ||
Channel memory _basic, | ||
Channel memory _incentivized | ||
) | ||
BaseDOTApp(_name, _symbol, _basic, _incentivized) | ||
{ } | ||
|
||
function granularity() pure internal override returns (uint256) { | ||
return GRANULARITY; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity >=0.7.6; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "@openzeppelin/contracts/token/ERC777/ERC777.sol"; | ||
import "@openzeppelin/contracts/access/Ownable.sol"; | ||
|
||
contract WrappedToken is ERC777, Ownable { | ||
|
||
constructor( | ||
string memory _name, | ||
string memory _symbol, | ||
address[] memory _defaultOperators | ||
) | ||
ERC777(_name, _symbol, _defaultOperators) | ||
{ } | ||
|
||
function burn(address sender, uint256 amount, bytes memory data) external onlyOwner { | ||
_burn(sender, amount, data, ""); | ||
} | ||
|
||
function mint(address recipient, uint256 amount, bytes memory data) external onlyOwner { | ||
_mint(recipient, amount, data, ""); | ||
} | ||
|
||
// Don't allow users to directly burn their SnowDOT via the IERC777 burn API, as it won't have | ||
// the desired effect. | ||
|
||
function burn(uint256, bytes memory) public pure override { | ||
revert("not-supported"); | ||
} | ||
|
||
function operatorBurn(address, uint256, bytes memory, bytes memory) public pure override { | ||
revert("not-supported"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// Example usage: | ||
// truffle exec burnWrappedDOT.js [amount] [polkadot-recipient] | ||
// truffle exec burnWrappedDOT.js [amount] [polkadot-recipient] --network ropsten | ||
|
||
const DOTApp = artifacts.require("DOTApp"); | ||
const Token = artifacts.require("Token"); | ||
|
||
const BigNumber = require('bignumber.js'); | ||
|
||
const DOT_DECIMALS = 10; | ||
const ETHER_DECIMALS = 18; | ||
|
||
const wrapped = (amount) => | ||
amount.multipliedBy(BigNumber(10).exponentiatedBy(ETHER_DECIMALS - DOT_DECIMALS)); | ||
|
||
const unwrapped = (amount) => | ||
amount.dividedToIntegerBy(BigNumber(10).exponentiatedBy(ETHER_DECIMALS - DOT_DECIMALS)); | ||
|
||
module.exports = async () => { | ||
// Parameters | ||
const amount = process.argv[4].toString(); | ||
if (!amount) { | ||
console.log("Must provide an amount (wrapped dots)") | ||
return | ||
} | ||
const amountWei = web3.utils.toWei(amount, "ether"); | ||
|
||
const polkadotRecipient = process.argv[5].toString(); | ||
if (!polkadotRecipient) { | ||
console.log("Must provide a Polkadot recipient") | ||
return | ||
} | ||
const recipient = Buffer.from(polkadotRecipient.replace(/^0x/, ""), "hex"); | ||
|
||
try { | ||
// Get current accounts | ||
const accounts = await web3.eth.getAccounts(); | ||
|
||
|
||
const account = accounts[1]; | ||
|
||
const app = await DOTApp.deployed(); | ||
const token = await Token.at(await app.token()); | ||
|
||
let totalSupply = BigNumber(await token.totalSupply()); | ||
console.log("Total Supply ", unwrapped(totalSupply).toString()); | ||
|
||
let balanceWei = await token.balanceOf(account); | ||
let balance = unwrapped(BigNumber(balanceWei)); | ||
|
||
console.log(`Balance for sending account ${account}: ${balanceWei} wei (${balance} wdot)`); | ||
|
||
return; | ||
|
||
const { logs } = await app.burn( | ||
recipient, | ||
amountWei, | ||
0, | ||
{ | ||
from: account, | ||
gas: 300000 // 300,000 Gwei | ||
} | ||
); | ||
|
||
console.log("Submitted transaction"); | ||
|
||
const event = logs.find(e => e.event === "Burned"); | ||
const event_decoded = { | ||
[event.event]: { | ||
sender: event.args.sender, | ||
recipient: event.args.recipient, | ||
amount: event.args.amount.toString(), | ||
} | ||
} | ||
console.log("Events:") | ||
console.log(JSON.stringify(event_decoded, null, 2)); | ||
|
||
balanceWei = await token.balanceOf(account); | ||
balance = web3.utils.fromWei(balanceWei); | ||
console.log(`Balance for ${account} is now: ${balanceWei} wei (${balance} wdot)`); | ||
|
||
} catch (error) { | ||
console.error({ error }); | ||
} | ||
return; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.