Skip to content

Commit

Permalink
Move tests to plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
dmtrskv committed Jul 18, 2024
1 parent 0531801 commit 6c39064
Show file tree
Hide file tree
Showing 7 changed files with 4,340 additions and 125 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ dist/
.tsbuildinfo
*.tar.gz
*.tgz
/cache/
/artifacts/
/typechain-types/
21 changes: 21 additions & 0 deletions contracts/Incrementer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./Nil.sol";

contract Incrementer is NilBase {
uint256 private value;

event ValueChanged(uint256 newValue);
receive() external payable {}

function increment() public onlyInternal payable {
value += 1;
emit ValueChanged(value);
}

function getValue() public view returns (uint256) {
return value;
}
}

156 changes: 156 additions & 0 deletions contracts/Nil.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library Nil {
uint private constant SEND_MESSAGE = 0xfc;
address private constant ASYNC_CALL = address(0xfd);
address public constant VERIFY_SIGNATURE = address(0xfe);
address public constant IS_INTERNAL_MESSAGE = address(0xff);
address private constant MINT_CURRENCY = address(0xd0);
address private constant GET_CURRENCY_BALANCE = address(0xd1);
address private constant SEND_CURRENCY_SYNC = address(0xd2);
address private constant GET_MESSAGE_TOKENS = address(0xd3);

address payable public constant MINTER_ADDRESS = payable(address(0x0001222222222222222222222222222222222222));

// Token is a struct that represents a token with an id and amount.
struct Token {
uint256 id;
uint256 amount;
}

// asyncCall is a function that makes an asynchronous call to `dst` contract.
function asyncCall(
address dst,
address refundTo,
address bounceTo,
uint gas,
bool deploy,
uint value,
bytes memory callData
) internal returns(bool) {
Token[] memory tokens;
return asyncCall(dst, refundTo, bounceTo, gas, deploy, value, tokens, callData);
}

// asyncCall is a function that makes an asynchronous call to `dst` contract.
// This function is used to call a contract with a list of tokens.
function asyncCall(
address dst,
address refundTo,
address bounceTo,
uint gas,
bool deploy,
uint value,
Token[] memory tokens,
bytes memory callData
) internal returns(bool) {
bool success = Precompile(ASYNC_CALL).precompileAsyncCall{value: value}(deploy, dst, refundTo, bounceTo, gas,
tokens, callData);
return success;
}

function syncCall(
address dst,
uint gas,
uint value,
Token[] memory tokens,
bytes memory callData
) internal returns(bool, bytes memory) {
if (tokens.length > 0) {
Precompile(SEND_CURRENCY_SYNC).precompileSendTokens(dst, tokens);
}
(bool success, bytes memory returnData) = dst.call{gas: gas, value: value}(callData);
return (success, returnData);
}

// Send raw internal message using a special precompiled contract
function sendMessage(uint g, bytes memory message) internal {
uint message_size = message.length;
assembly {
// Call precompiled contract.
// Arguments: gas, precompiled address, value, input, input size, output, output size
if iszero(call(g, SEND_MESSAGE, 0, add(message, 32), message_size, 0, 0)) {
revert(0, 0)
}
}
}

// Function to call the validateSignature precompiled contract
function validateSignature(
bytes memory pubkey,
uint256 hash,
bytes memory signature
) internal view returns (bool) {
// ABI encode the input parameters
bytes memory encodedInput = abi.encode(pubkey, hash, signature);
bool success;
bool result;

// Perform the static call to the precompiled contract at address `VerifyExternalMessage`
bytes memory returnData;
(success, returnData) = VERIFY_SIGNATURE.staticcall(encodedInput);

require(success, "Precompiled contract call failed");

// Extract the boolean result from the returned data
if (returnData.length > 0) {
result = abi.decode(returnData, (bool));
}

return result;
}

// mintCurrency mints a token with a given id and amount. Can be called only by the special minter contract.
// Returns `true` if the minting was successful.
function mintToken(uint256 id, uint256 amount) internal returns(bool) {
return Precompile(MINT_CURRENCY).precompileMintCurrency(id, amount);
}

// getCurrencyBalance returns the balance of a token with a given id for a given address.
function tokensBalance(address addr, uint256 id) internal returns(uint256) {
return Precompile(GET_CURRENCY_BALANCE).precompileGetCurrencyBalance(id, addr);
}

// msgTokens returns tokens from the current message.
function msgTokens() internal returns(Token[] memory) {
return Precompile(GET_MESSAGE_TOKENS).precompileGetMessageTokens();
}
}

// NilBase is a base contract that provides modifiers for checking the type of message (internal or external).
contract NilBase {
// Check that method was invoked from internal message
modifier onlyInternal() {
require(isInternalMessage(), "Trying to call internal function with external message");
_;
}

// Check that method was invoked from external message
modifier onlyExternal() {
require(!isInternalMessage(), "Trying to call external function with internal message");
_;
}

function isInternalMessage() internal view returns (bool) {
bytes memory data;
(bool success, bytes memory returnData) = Nil.IS_INTERNAL_MESSAGE.staticcall(data);
require(success, "Precompiled contract call failed");
require(returnData.length > 0, "'IS_INTERNAL_MESSAGE' returns invalid data");
return abi.decode(returnData, (bool));
}
}

// Precompile is a contract that provides stubs for precompiled contract calls.
// NOTE: Function should always return value, otherwise Solidity will check contract existence by EXTCODESIZE opcode
contract Precompile {
function precompileMintCurrency(uint256 id, uint256 amount) public returns(bool) {}
function precompileGetCurrencyBalance(uint256 id, address addr) public returns(uint256) {}
function precompileAsyncCall(bool, address, address, address, uint, Nil.Token[] memory, bytes memory) public payable returns(bool) {}
function precompileSendTokens(address, Nil.Token[] memory) public returns(bool) {}
function precompileGetMessageTokens() public returns(Nil.Token[] memory) {}
}

abstract contract NilBounceable is NilBase {
function bounce(string calldata err) virtual payable external;
}
23 changes: 23 additions & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import "@nomicfoundation/hardhat-ethers";
import "@nomicfoundation/hardhat-toolbox";
import * as dotenv from "dotenv";
import ".";
import {NilHardhatUserConfig} from "./src";

dotenv.config();

const config: NilHardhatUserConfig = {
solidity: "0.8.24",
ignition: {
requiredConfirmations: 1,
},
networks: {
nil: {
url: process.env.NIL_RPC_ENDPOINT,
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
},
},
walletAddress: process.env.WALLET_ADDR,
debug: true,
};
export default config;
Loading

0 comments on commit 6c39064

Please sign in to comment.