added version of bandwidth generator which doesn't interact with Gravity Bridge

This commit is contained in:
mfahampshire
2022-02-03 16:10:28 +01:00
parent 55762b52b9
commit 3cfdb56f5d
9 changed files with 424 additions and 20 deletions
@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../../build-info/0bafc698027e3d830c902762382d89e8.json"
"buildInfo": "../../../../../build-info/88ee929c429ffa38bc6e1d1dbccfcb81.json"
}
@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../../build-info/0bafc698027e3d830c902762382d89e8.json"
"buildInfo": "../../../../../build-info/88ee929c429ffa38bc6e1d1dbccfcb81.json"
}
@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../../../build-info/0bafc698027e3d830c902762382d89e8.json"
"buildInfo": "../../../../../../build-info/88ee929c429ffa38bc6e1d1dbccfcb81.json"
}
@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/0bafc698027e3d830c902762382d89e8.json"
"buildInfo": "../../../../build-info/88ee929c429ffa38bc6e1d1dbccfcb81.json"
}
@@ -580,6 +580,44 @@
"artifacts": [
"Strings"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/BandwidthGeneratorNoGravity.sol": {
"lastModificationDate": 1643900677524,
"contentHash": "4211e4c7907b037c3b01eb2981cc4934",
"sourceName": "contracts/BandwidthGeneratorNoGravity.sol",
"solcConfig": {
"version": "0.8.10",
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers"
],
"": [
"ast"
]
}
}
}
},
"imports": [
"./CosmosToken.sol",
"@openzeppelin/contracts/access/Ownable.sol",
"@openzeppelin/contracts/utils/math/SafeMath.sol"
],
"versionPragmas": [
"0.8.10"
],
"artifacts": [
"BandwidthGeneratorNoGravity"
]
}
}
}
@@ -1,14 +1 @@
{
"rinkeby":{
"NYM_ERC20":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B",
"BANDWIDTH_GENERATOR":"0x5FbDB2315678afecb367f032d93F642f64180aa3",
"GRAVITY":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B"
},
"mainnet":{
"NYM_ERC20":"0x525A8F6F3Ba4752868cde25164382BfbaE3990e1",
"NYMT": "0xE8883BAeF3869e14E4823F46662e81D4F7d2A81F",
"BANDWIDTH_GENERATOR":"",
"BANDWIDTH_GENERATOR_NYMT":"0xB3BF30DD53044c9050B7309031Bbf26b2cecF3be",
"GRAVITY":"0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906"
}
}
{"rinkeby":{"NYM_ERC20":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B","BANDWIDTH_GENERATOR":"0xd33225Dd78523651C5563aEae5FC309c214bf464","GRAVITY":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B"},"mainnet":{"NYM_ERC20":"0x525A8F6F3Ba4752868cde25164382BfbaE3990e1","NYMT":"0xE8883BAeF3869e14E4823F46662e81D4F7d2A81F","BANDWIDTH_GENERATOR":"","BANDWIDTH_GENERATOR_NYMT":"0xB3BF30DD53044c9050B7309031Bbf26b2cecF3be","GRAVITY":"0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906"}}
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.10;
import "./CosmosToken.sol";
// import "./Gravity.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
/**
* @title BandwidthGenerator
* @dev Contract for generating Basic Bandwidth Credentials (BBCs) on the Nym cosmos blockchain,
* using ERC20 representations of NYM as payment. Utilises the Gravity Bridge for cross-chain payment.
*
* Credential generation can be switched on/off by the contract owner.
*
* Credentials represent a certain amount of bandwidth which can be sent through the Nym Mixnet.
* By default 1 NYM = 1 GB of bandwidth. The `BytesPerToken` amount can be adjusted by the contract owner.
* The amount of bandwidth bought is calculated according to the following formula:
* `(Token amount in 'wei' / 10**18) * BytesPerToken`
*/
contract BandwidthGeneratorNoGravity is Ownable {
using SafeMath for uint256;
CosmosERC20 public erc20;
// Gravity public gravityBridge;
uint256 public BytesPerToken;
bool public credentialGenerationEnabled;
event BBCredentialPurchased(
uint256 Bandwidth,
uint256 indexed VerificationKey,
bytes SignedVerificationKey,
string CosmosRecipient
);
event RatioChanged(
uint256 indexed NewBytesPerToken
);
event CredentialGenerationSwitch(
bool Enabled
);
modifier checkEnabled() {
require(credentialGenerationEnabled, "BandwidthGenerator: credential generation isn't currently enabled");
_;
}
/**
* @param _erc20 Address of the erc20NYM deployed through the Gravity Bridge.
*/
constructor(CosmosERC20 _erc20) {
require(address(_erc20) != address(0), "BandwidthGenerator: erc20 address cannot be null");
// require(address(_gravityBridge) != address(0), "BandwidthGenerator: gravity bridge address cannot be null");
erc20 = _erc20;
// gravityBridge = _gravityBridge;
BytesPerToken = 1073741824; // default amount set at deployment: 1 erc20NYM = 1073741824 Bytes = 1GB
credentialGenerationEnabled = true;
}
/**
* @dev Changes amount of Bytes each erc20NYM is tradable for. Can only be called by Owner.
* @param _newBytesPerTokenAmount Amount of Bytes BBC is worth per 1 erc20NYM token.
*/
function changeRatio(uint256 _newBytesPerTokenAmount) public onlyOwner {
require(_newBytesPerTokenAmount != 0, "BandwidthGenerator: price cannot be 0");
BytesPerToken = _newBytesPerTokenAmount;
emit RatioChanged(_newBytesPerTokenAmount);
}
/**
* @dev Switches credential generation on/off. Can only be called by Owner.
* @param _generation Whether credential generation is turned on/off.
*/
function credentialGenerationSwitch(bool _generation) public onlyOwner {
credentialGenerationEnabled = _generation;
emit CredentialGenerationSwitch(_generation);
}
/**
* @dev Function to create a BBC for account owning the verification key on the Nym Cosmos Blockchain
* by transfering erc20NYM via the Gravity Bridge.
* @param _amount Amount of erc20NYM tokens to spend on BBC - denominated in wei.
* @param _verificationKey Verification key of account on Nym blockchain who is purchasing BBC.
* @param _signedVerificationKey Number of erc20NYMs to spend signed by _verificationKey for auth on Cosmos Blockchain.
* @param _cosmosRecipient Address of the recipient of payment on Nym Cosmos Blockchain.
*/
function generateBasicBandwidthCredential(uint256 _amount, uint256 _verificationKey, bytes memory _signedVerificationKey, string calldata _cosmosRecipient) public checkEnabled {
require(_signedVerificationKey.length == 64, "BandwidthGenerator: Signature doesn't have 64 bytes");
erc20.transferFrom(msg.sender, address(this), _amount);
// erc20.approve(address(gravityBridge), _amount);
// gravityBridge.sendToCosmos(
// address(erc20),
// _cosmosRecipient,
// _amount
// );
uint256 bandwidth = bandwidthFromToken(_amount);
emit BBCredentialPurchased(
bandwidth,
_verificationKey,
_signedVerificationKey,
_cosmosRecipient
);
}
function bandwidthFromToken(uint256 _amount) public view returns (uint256) {
uint256 amountMulBytes = _amount.mul(BytesPerToken);
return amountMulBytes.div(10**18);
}
}
@@ -21,8 +21,7 @@ module.exports = {
// },
gasReporter: {
currency: 'EUR',
gasPrice: 21,
token: 'GWEI'
gasPrice: 78
},
networks: {
localhost: {
@@ -0,0 +1,266 @@
const { expect } = require("chai");
const { constants, expectRevert, expectEvent } = require('@openzeppelin/test-helpers');
const { artifacts, web3 } = require("hardhat");
const BN = require('bn.js');
const BandwidthGenerator = artifacts.require('BandwidthGeneratorNoGravity');
const Gravity = artifacts.require('test-contracts/TestGravity');
const CosmosToken = artifacts.require('TestCosmosERC20');
contract('BandwidthGenerator', (accounts) => {
let bandwidthGenerator;
let gravity;
let erc20token;
let owner = accounts[0];
let user = accounts[1];
let cosmosRecipient = "nymt1f06hzmwf9chqewkpv93ajk6tayzp4784m2da9x"; // random sandbox testnet address
let initialRatio = 1073741824; // 1073741824 bytes = 1GB
let newRatio;
let tokenAmount = web3.utils.toWei('100'); // this is converting 100 tokens to their representation in wei: 100000000000000000000
let halfTokenAmount = web3.utils.toWei('50');
let unevenTokenAmount = web3.utils.toWei('11.5'); // 11500000000000000000
let oneToken = web3.utils.toWei('1');
before('deploy contracts', async () => {
// deploy gravity bridge with test data
gravity = await Gravity.new(
constants.ZERO_BYTES32,
[owner],
[2863311531]
);
// deploy erc20 NYM from bridge
await gravity.deployERC20(
'cosmosNYMDenomination',
'NYMERC20',
'NYM',
6
);
// grab event args for getting token address
const logs = await gravity.getPastEvents({
fromBlock: 0,
toBlock: "latest",
});
// create contract abstraction of deployed erc20NYM with address from event args
erc20token = await CosmosToken.at(logs[0].args._tokenContract);
// deploy bandwidthGenerator contract with contract address of erc20NYM & address of gravity bridge
bandwidthGenerator = await BandwidthGenerator.new(erc20token.address);
});
context(">> deployment parameters are valid", () => {
it("returns the correct erc20 address", async () => {
expect((await bandwidthGenerator.erc20()).toString()).to.equal((erc20token.address).toString());
});
// it("returns the correct gravity address", async () => {
// expect((await bandwidthGenerator.gravityBridge()).toString()).to.equal((gravity.address).toString());
// });
it("returns the correct initial BytesPerToken ratio", async () => {
expect((await bandwidthGenerator.BytesPerToken()).toString()).to.equal((initialRatio).toString());
});
it("returns the correct contract admin", async () => {
expect((await bandwidthGenerator.owner()).toString()).to.equal((owner).toString());
});
it("returns the correct default generation state: true", async () => {
expect((await bandwidthGenerator.credentialGenerationEnabled())).to.equal(true);
});
});
context(">> deployment parameters are invalid", () => {
it("cannot be deployed with invalid erc20 address (zero address)", async () => {
expectRevert(
BandwidthGenerator.new(constants.ZERO_ADDRESS),
"BandwidthGenerator: erc20 address cannot be null"
)
});
// it("cannot be deployed with invalid gravity bridge address (zero address)", async () => {
// expectRevert(
// BandwidthGenerator.new(erc20token.address, constants.ZERO_ADDRESS),
// "BandwidthGenerator: gravity bridge address cannot be null"
// )
// });
});
context(">> generateBasicBandwidthCredential()", () => {
before("mint tokens & approve", async () => {
// transfer tokens to account which will create a BBCredential
await erc20token.mintForUnitTesting(user, tokenAmount);
// approve transfer to contract
await erc20token.approve(bandwidthGenerator.address,(tokenAmount),{ from: user });
});
it("transfers tokens to bridge and emits an event with the correct values: 50 erc20NYM = 50GB of bandwidth", async () => {
let tx = await bandwidthGenerator.generateBasicBandwidthCredential(
halfTokenAmount,
15,
[0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba,
0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba],
cosmosRecipient,
{ from: user }
);
let expectedBandwidthInMB = ((halfTokenAmount/10**18)*initialRatio); // 50 * (1024*1024*1024) bytes = 51200MB = 50GB of bandwidth
await expectEvent.inTransaction(tx.tx, bandwidthGenerator, 'BBCredentialPurchased', {
Bandwidth: expectedBandwidthInMB.toString(),
VerificationKey: '15',
SignedVerificationKey: '0x39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba',
CosmosRecipient: cosmosRecipient
});
await expectEvent.inTransaction(tx.tx, erc20token, 'Transfer', {
from: user,
to: bandwidthGenerator.address,
});
// await expectEvent.inTransaction(tx.tx, gravity, 'SendToCosmosEvent', {
// _tokenContract: erc20token.address,
// _sender: bandwidthGenerator.address,
// _destination: cosmosRecipient,
// _amount: halfTokenAmount
// });
expect((await erc20token.balanceOf(bandwidthGenerator.address)).toString()).to.equal(halfTokenAmount.toString());
expect((await erc20token.balanceOf(user)).toString()).to.equal(halfTokenAmount.toString());
});
/**
* This can be out by a float still with amounts such as '.1'
*/
it("it transfers for uneven token amounts", async () => {
let tx = await bandwidthGenerator.generateBasicBandwidthCredential(
unevenTokenAmount,
15,
[0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba,
0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba],
cosmosRecipient,
{ from: user }
);
let newexpectedBandwidthInMB = ((11500000000000000000*initialRatio)/10**18);
await expectEvent.inTransaction(tx.tx, bandwidthGenerator, 'BBCredentialPurchased', {
Bandwidth: newexpectedBandwidthInMB.toString(),
VerificationKey: '15',
SignedVerificationKey: '0x39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba',
CosmosRecipient: cosmosRecipient
});
await expectEvent.inTransaction(tx.tx, erc20token, 'Transfer', {
from: user,
to: bandwidthGenerator.address,
});
// await expectEvent.inTransaction(tx.tx, gravity, 'SendToCosmosEvent', {
// _tokenContract: erc20token.address,
// _sender: bandwidthGenerator.address,
// _destination: cosmosRecipient,
// _amount: unevenTokenAmount
// });
// });
it("reverts when signed verification key !=64 bytes", async () => {
await erc20token.approve(bandwidthGenerator.address,(halfTokenAmount),{ from: user });
await expectRevert(
bandwidthGenerator.generateBasicBandwidthCredential(
1,
16,
[0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba,
0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba],
cosmosRecipient,
{ from: user }
), "BandwidthGenerator: Signature doesn't have 64 bytes"
);
});
});
context(">> changeRatio()", () => {
it("only admin can change token to MB ratio", async () => {
newRatio = 10 * initialRatio; // 10GB of bandwidth per 1 erc20NYM
await expectRevert(
bandwidthGenerator.changeRatio(newRatio, {from: user}),
"Ownable: caller is not the owner"
);
});
it("admin can change ratio, emits 'RatioChanged' event", async () => {
let tx = await bandwidthGenerator.changeRatio(newRatio, {from: owner});
await expectEvent.inTransaction(tx.tx, bandwidthGenerator, 'RatioChanged', {
NewBytesPerToken: newRatio.toString()
});
expect((await bandwidthGenerator.BytesPerToken()).toString()).to.equal((newRatio).toString());
});
it("BBCredential represents new ratio after change: 1 erc20NYM = 10GB of bandwidth", async () => {
let tx = await bandwidthGenerator.generateBasicBandwidthCredential(
oneToken,
15,
[0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba,
0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba],
cosmosRecipient,
{ from: user }
);
let expectedBandwidthInMB = ((oneToken/10**18)*newRatio);
await expectEvent.inTransaction(tx.tx, bandwidthGenerator, 'BBCredentialPurchased', {
Bandwidth: expectedBandwidthInMB.toString(),
VerificationKey: '15',
SignedVerificationKey: '0x39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba',
CosmosRecipient: cosmosRecipient
});
await expectEvent.inTransaction(tx.tx, erc20token, 'Transfer', {
from: user,
to: bandwidthGenerator.address,
});
// await expectEvent.inTransaction(tx.tx, gravity, 'SendToCosmosEvent', {
// _tokenContract: erc20token.address,
// _sender: bandwidthGenerator.address,
// _destination: cosmosRecipient,
// _amount: oneToken
// });
});
});
context(">>credential generation admin switch", () => {
it("only admin can switch credential generation off", async () => {
await expectRevert(
bandwidthGenerator.credentialGenerationSwitch(false, { from: user }),
"Ownable: caller is not the owner"
)
});
it("admin can switch credential generation on/off & switch generates an event", async () => {
let tx = await bandwidthGenerator.credentialGenerationSwitch(false);
expect((await bandwidthGenerator.credentialGenerationEnabled())).to.equal(false);
await expectEvent.inTransaction(tx.tx, bandwidthGenerator, 'CredentialGenerationSwitch', {
Enabled: false,
});
tx = await bandwidthGenerator.credentialGenerationSwitch(true);
expect((await bandwidthGenerator.credentialGenerationEnabled())).to.equal(true);
await expectEvent.inTransaction(tx.tx, bandwidthGenerator, 'CredentialGenerationSwitch', {
Enabled: true,
});
});
it("cannot generate credentials if switch = false", async () => {
await bandwidthGenerator.credentialGenerationSwitch(false);
await expectRevert(
bandwidthGenerator.generateBasicBandwidthCredential(
oneToken,
15,
[0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba,
0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba],
cosmosRecipient,
{ from: user }
), "BandwidthGenerator: credential generation isn't currently enabled"
);
});
});
});
})