Skip to content

Commit

Permalink
improve vthobox logic, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
laalaguer committed Jun 27, 2021
1 parent 5cabc84 commit f6a8164
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 30 deletions.
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# Compile Contracts
compile:
. .env/bin/activate && cd vvet && brownie compile
. .env/bin/activate && cd vvet && brownie compile

test:
. .env/bin/activate && cd vvet && python3 -m pytest -vv -s
# Install compiler tools
install:
npm install -g ganache-cli
python3 -m venv .env
. .env/bin/activate && pip3 install wheel
. .env/bin/activate && pip3 install -r requirements.txt
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
eth-brownie
pytest
thor-requests
67 changes: 38 additions & 29 deletions vvet/contracts/VTHOBox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,72 +6,81 @@ pragma solidity ^0.8.0;

// It can also track the in-and-out of user's VTHO.

// uint104 - safe for vtho to operate another 100 years.
// Data structure concern:
// uint48 - enough to store 30,000+ years.
// uint104 - enough to store whole vet on VeChain.
// uint104 - enough to store vtho for 100+ years.
contract VTHOBox {

struct User {
uint40 lastUpdatedTime;
uint104 vetBalance;
uint104 vthoBalance;
uint104 balance; // vet in wei
uint104 energy; // vtho in wei
uint48 lastUpdatedTime;
}

mapping(address => User) private users;

function addVET(address addr, uint104 amount) internal {
modifier restrict(uint256 amount) {
require(amount < type(uint104).max, "value should < type(uint104).max");
_;
}

function addVET(address addr, uint256 amount) restrict(amount) internal {
_update(addr);
users[addr].vetBalance += amount;
users[addr].balance += uint104(amount);
}

function removeVET(address addr, uint104 amount) internal {
function removeVET(address addr, uint256 amount) restrict(amount) internal {
_update(addr);
users[addr].vetBalance -= amount;
users[addr].balance -= uint104(amount);
}

function vetBalance(address addr) public view returns (uint104 amount) {
return users[addr].vetBalance;
function vetBalance(address addr) public view returns (uint256 amount) {
return users[addr].balance;
}

function addVTHO(address addr, uint104 amount) internal {
function addVTHO(address addr, uint256 amount) restrict(amount) internal {
_update(addr);
users[addr].vthoBalance += amount;
users[addr].energy += uint104(amount);
}

function removeVTHO(address addr, uint104 amount) internal {
function removeVTHO(address addr, uint256 amount) restrict(amount) internal {
_update(addr);
users[addr].vthoBalance -= amount;
users[addr].energy -= uint104(amount);
}

function vthoBalance(address addr) public returns (uint104 amount) {
_update(addr);
return users[addr].vthoBalance;
function vthoBalance(address addr) public view returns (uint256 amount) {
if (users[addr].lastUpdatedTime == 0) {
return 0;
}
return users[addr].energy + calculateVTHO(users[addr].lastUpdatedTime, uint48(block.timestamp), users[addr].balance);
}

// Sync the vtho balance that the address has
// up till current block (timestamp)
function _update(address addr) internal {
// Sync the vtho balance that the address has up till current block (timestamp)
function _update(address addr) public {
if (users[addr].lastUpdatedTime > 0) {
assert(users[addr].lastUpdatedTime <= uint40(block.timestamp));
users[addr].vthoBalance += calculateVTHO(
assert(users[addr].lastUpdatedTime <= uint48(block.timestamp));
users[addr].energy += calculateVTHO(
users[addr].lastUpdatedTime,
uint40(block.timestamp),
users[addr].vetBalance
uint48(block.timestamp),
users[addr].balance
);
}

users[addr].lastUpdatedTime = uint40(block.timestamp);
users[addr].lastUpdatedTime = uint48(block.timestamp);
}

// Calculate vtho generated between time t1 and t2
// @param t1 Time in seconds
// @param t2 Time in seconds
// @param vetBalance VET in wei
// @param vetAmount VET in wei
// @return vtho generated in wei
function calculateVTHO(
uint40 t1,
uint40 t2,
uint48 t1,
uint48 t2,
uint104 vetAmount
) public pure returns (uint104 vtho) {
require(t1 < t2);
require(t1 < t2, "t1 should be < t2");
return ((vetAmount * 5) / (10**9)) * (t2 - t1);
}
}
3 changes: 3 additions & 0 deletions vvet/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[pytest]
testpaths =
tests
Empty file added vvet/tests/__init__.py
Empty file.
81 changes: 81 additions & 0 deletions vvet/tests/fixtures.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import pytest
from thor_requests.connect import Connect
from thor_requests.contract import Contract
from thor_requests.wallet import Wallet


@pytest.fixture
def vtho_contract_address():
return "0x0000000000000000000000000000456e65726779"


@pytest.fixture
def solo_connector():
endpoints = ["http://localhost:8669", "http://solo.veblocks.net"]
for x in endpoints:
c = Connect(x)
try:
c.get_chainTag()
return c
except:
continue

raise Exception("Cannot connect to a reliable solo node to run tests")


@pytest.fixture
def solo_wallet():
return Wallet.fromMnemonic(
[
"denial",
"kitchen",
"pet",
"squirrel",
"other",
"broom",
"bar",
"gas",
"better",
"priority",
"spoil",
"cross",
]
)


@pytest.fixture
def testnet_connector():
return Connect("http://testnet.veblocks.net")


@pytest.fixture
def mainnet_connector():
return Connect("http://mainnet.veblocks.net")


@pytest.fixture
def testnet_wallet():
return Wallet.fromPrivateKey(
bytes.fromhex(
"dce1443bd2ef0c2631adc1c67e5c93f13dc23a41c18b536effbbdcbcdb96fb65"
)
)


@pytest.fixture
def mainnet_wallet():
return Wallet.fromPrivateKey(
bytes.fromhex(
"dce1443bd2ef0c2631adc1c67e5c93f13dc23a41c18b536effbbdcbcdb96fb65"
)
)


@pytest.fixture
def clean_wallet():
return Wallet.newWallet()


@pytest.fixture
def vthobox_contract():
return Contract.fromFile("build/contracts/VTHOBox.json")
132 changes: 132 additions & 0 deletions vvet/tests/test_gas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import time
import pytest
from thor_requests import utils
from .fixtures import (
testnet_connector,
testnet_wallet,
vthobox_contract
)

@pytest.fixture(autouse=True)
def deploy_contract(testnet_connector, testnet_wallet, vthobox_contract):
''' test the storage gas '''
res = testnet_connector.deploy(testnet_wallet, vthobox_contract, None, None, 0)
assert "id" in res # Should contain a {'id': '0x...' }
# print(f"deploy_vvet_tx_id: {res['id']}")
tx_id = res["id"]
# Should have the deployed contract address now
receipt = testnet_connector.wait_for_tx_receipt(tx_id)
created_contracts = utils.read_created_contracts(receipt)
assert len(created_contracts) == 1
# print(f"created_vvet_address: {created_contracts[0]}")
return created_contracts[0]

def test_store(deploy_contract, testnet_connector, testnet_wallet, vthobox_contract):
''' Transaction of add some VET '''
print("wallet:addr:", testnet_wallet.getAddress())
contract_addr = deploy_contract
print("contract:addr:", contract_addr)

# Call to get the balance of user's vet
res = testnet_connector.call(
testnet_wallet.getAddress(),
vthobox_contract,
"vetBalance",
[testnet_wallet.getAddress()],
contract_addr
)
assert res["reverted"] == False
assert res["decoded"]["0"] == 0 # nothing added, so no vet is there
print('call:vetBalance:gas:', res["gasUsed"])

# Call to get the balance of user's vtho
res = testnet_connector.call(
testnet_wallet.getAddress(),
vthobox_contract,
"vthoBalance",
[testnet_wallet.getAddress()],
contract_addr
)
assert res["reverted"] == False
assert res["decoded"]["0"] == 0 # nothing added, so no vtho is there
print('call:vthoBalance:gas:', res["gasUsed"])

# Add 3 vet for the first time
res = testnet_connector.transact(
testnet_wallet,
vthobox_contract,
"addVET",
[testnet_wallet.getAddress(), 3 * (10 ** 18)],
contract_addr
)
assert res["id"]

tx_id = res["id"]
receipt = testnet_connector.wait_for_tx_receipt(tx_id)
print('transact:addVET:gas:', receipt["gasUsed"])

time.sleep(12)

# Call to get the balance of user's vet
res = testnet_connector.call(
testnet_wallet.getAddress(),
vthobox_contract,
"vetBalance",
[testnet_wallet.getAddress()],
contract_addr
)
assert res["reverted"] == False
assert res["decoded"]["0"] == 3 * (10 ** 18) # 3 vet should be there
print('call:vetBalance:gas:', res["gasUsed"])

# Call to get the balance of user's vtho
res = testnet_connector.call(
testnet_wallet.getAddress(),
vthobox_contract,
"vthoBalance",
[testnet_wallet.getAddress()],
contract_addr
)
assert res["reverted"] == False
assert res["decoded"]["0"] > 0 # Some vtho should be there
print('call:vthoBalance:gas:', res["gasUsed"])

# Add more VET (3) to the user
res = testnet_connector.transact(
testnet_wallet,
vthobox_contract,
"addVET",
[testnet_wallet.getAddress(), 3 * (10 ** 18)],
contract_addr
)
assert res["id"]

tx_id = res["id"]
receipt = testnet_connector.wait_for_tx_receipt(tx_id)
print('transact:addVET:gas:', receipt["gasUsed"])

time.sleep(12)

# Call to get the balance of user's vet
res = testnet_connector.call(
testnet_wallet.getAddress(),
vthobox_contract,
"vetBalance",
[testnet_wallet.getAddress()],
contract_addr
)
assert res["reverted"] == False
assert res["decoded"]["0"] == 6 * (10 ** 18) # 6 vet should be there
print('call:vetBalance:gas:', res["gasUsed"])

# Call to get the balance of user's vtho
res = testnet_connector.call(
testnet_wallet.getAddress(),
vthobox_contract,
"vthoBalance",
[testnet_wallet.getAddress()],
contract_addr
)
assert res["reverted"] == False
assert res["decoded"]["0"] > 0 # Some vtho should be there
print('call:vthoBalance:gas:', res["gasUsed"])

0 comments on commit f6a8164

Please sign in to comment.