From 368844ad2a28ec07848e3c0169cf2b83b579a2e8 Mon Sep 17 00:00:00 2001
From: Aaron Buchwald <aaron.buchwald56@gmail.com>
Date: Mon, 23 Nov 2020 20:46:35 -0500
Subject: Add native asset precompiled contracts for apricot release

---
 contracts/arc20.sol           | 150 ++++++++++++++++++++++++++++++++++++++
 contracts/nativeAssets.sol    |  20 +++++
 core/vm/contracts.go          |  86 +++++++++++-----------
 core/vm/contracts_stateful.go | 165 ++++++++++++++++++++++++++++++++++++++++++
 core/vm/evm.go                |  22 +++---
 core/vm/interpreter.go        |  12 +++
 core/vm/jump_table.go         |  11 +++
 core/vm/opcodes.go            |   1 +
 eth/backend.go                |   5 +-
 examples/arc20/main.go        |  93 ++++++++++++++++++++++++
 examples/multicoin/main.go    |  17 +++--
 params/config.go              |  67 ++++++++++++++++-
 params/protocol_params.go     |   2 +
 plugin/evm/export_tx.go       |   3 +-
 plugin/evm/import_tx.go       |   3 +-
 plugin/evm/vm.go              |   9 +++
 16 files changed, 597 insertions(+), 69 deletions(-)
 create mode 100644 contracts/arc20.sol
 create mode 100644 contracts/nativeAssets.sol
 create mode 100644 core/vm/contracts_stateful.go
 create mode 100644 examples/arc20/main.go

diff --git a/contracts/arc20.sol b/contracts/arc20.sol
new file mode 100644
index 0000000..a7fc997
--- /dev/null
+++ b/contracts/arc20.sol
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity >=0.6.0 <0.8.0;
+
+import {NativeAssets} from "./NativeAssets.sol";
+
+contract ARC20 {
+
+    mapping (address => uint256) private _balances;
+    mapping(address => mapping(address => uint256)) private _allowances;
+
+    uint256 private _assetID;
+
+    uint256 private _totalSupply;
+
+    string private _name;
+    string private _symbol;
+    uint8 private _decimals;
+
+    constructor(string memory name_, string memory symbol_, uint8 decimals_, uint256 assetID_) public {
+        _name = name_;
+        _symbol = symbol_;
+        _decimals = decimals_;
+        _assetID = assetID_;
+    }
+
+    /**
+     * @dev Returns the name of the token.
+     */
+    function name() public view returns (string memory) {
+        return _name;
+    }
+
+    /**
+     * @dev Returns the symbol of the token, usually a shorter version of the
+     * name.
+     */
+    function symbol() public view returns (string memory) {
+        return _symbol;
+    }
+
+    /**
+     * @dev Returns the number of decimals used to represent the token.
+     */
+    function decimals() public view returns (uint8) {
+        return _decimals;
+    }
+
+    /**
+     * @dev Returns the total supply of `assetID` currently held by
+     * this contract.
+     */
+    function totalSupply() public view returns (uint256) {
+        return _totalSupply;
+    }
+
+    /**
+     * @dev Returns the balance of `account` held in this contract.
+     */
+    function balanceOf(address account) public view returns (uint256) {
+        return _balances[account];
+    }
+
+    // Withdrawal/Deposit functionality
+
+    /**
+     * @dev Acknowledges the receipt of some amount of an Avalanche Native Token
+     * into the contract implementing this interface.
+     */
+    function deposit() public {
+        uint256 updatedBalance = NativeAssets.assetBalance(address(this), _assetID);
+        uint256 depositAmount = updatedBalance - _totalSupply;
+        assert(depositAmount >= 0);
+
+        _balances[msg.sender] += depositAmount;
+        _totalSupply = updatedBalance;
+        emit Deposit(msg.sender, depositAmount);
+    }
+
+    /**
+     * @dev Emitted when `value` tokens are deposited from `depositor`
+     */
+    event Deposit(address indexed depositor, uint256 value);
+
+    /**
+     * @dev Withdraws `value` of the underlying asset to the contract
+     * caller.
+     */
+    function withdraw(uint256 value) public {
+        require(_balances[msg.sender] >= value, "Insufficient funds for withdrawal");
+        
+        _balances[msg.sender] -= value;
+        _totalSupply -= value;
+
+        NativeAssets.assetCall(msg.sender, _assetID, value, "");
+        emit Withdrawal(msg.sender, value);
+    }
+
+    /**
+     * @dev Emitted when `value` tokens are withdrawn to `withdrawer`
+     */
+    event Withdrawal(address indexed withdrawer, uint256 value);
+
+    /**
+     * @dev Returns the `assetID` of the underlying asset this contract handles.
+     */
+    function assetID() external view returns (uint256) {
+        return _assetID;
+    }
+
+    event Transfer(address indexed from, address indexed to, uint256 value);
+
+    function transfer(address to, uint256 value) public returns (bool success) {
+        require(_balances[msg.sender] >= value, "insufficient balance for transfer");
+
+        _balances[msg.sender] -= value;  // deduct from sender's balance
+        _balances[to] += value;          // add to recipient's balance
+        emit Transfer(msg.sender, to, value);
+        return true;
+    }
+
+    event Approval(address indexed owner, address indexed spender, uint256 value);
+
+    function approve(address spender, uint256 value)
+        public
+        returns (bool success)
+    {
+        _allowances[msg.sender][spender] = value;
+        emit Approval(msg.sender, spender, value);
+        return true;
+    }
+
+    function transferFrom(address from, address to, uint256 value)
+        public
+        returns (bool success)
+    {
+        require(value <= _balances[from], "From address has insufficient balance to transfer");
+        require(value <= _allowances[from][msg.sender], "Insufficient allowance granted to sender");
+
+        _balances[from] -= value;
+        _balances[to] += value;
+        _allowances[from][msg.sender] -= value;
+        emit Transfer(from, to, value);
+        return true;
+    }
+
+    function allowance(address owner, address spender) public view returns (uint256) {
+        return _allowances[owner][spender];
+    }
+}
diff --git a/contracts/nativeAssets.sol b/contracts/nativeAssets.sol
new file mode 100644
index 0000000..0656271
--- /dev/null
+++ b/contracts/nativeAssets.sol
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: MIT
+
+pragma solidity >=0.6.0 <0.8.0;
+
+library NativeAssets {
+    address constant balanceAddr = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
+    address constant transferAddr = 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE;
+
+    function assetBalance(address addr, uint256 assetID) public returns (uint256) {
+        (bool success, bytes memory data) = balanceAddr.call(abi.encodePacked(addr, assetID));
+        require(success, "assetBalance failed");
+        return abi.decode(data, (uint256));
+    }
+    
+    function assetCall(address addr, uint256 assetID, uint256 assetAmount, bytes memory callData) public returns (bytes memory) {
+        (bool success, bytes memory data) = transferAddr.call(abi.encodePacked(addr, assetID, assetAmount, callData));
+        require(success, "assetCall failed");
+        return data;
+    }
+}
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 9827bac..6fc6334 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -44,61 +44,61 @@ type PrecompiledContract interface {
 
 // PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
 // contracts used in the Frontier and Homestead releases.
-var PrecompiledContractsHomestead = map[common.Address]PrecompiledContract{
-	common.BytesToAddress([]byte{1}): &ecrecover{},
-	common.BytesToAddress([]byte{2}): &sha256hash{},
-	common.BytesToAddress([]byte{3}): &ripemd160hash{},
-	common.BytesToAddress([]byte{4}): &dataCopy{},
+var PrecompiledContractsHomestead = map[common.Address]StatefulPrecompiledContract{
+	common.BytesToAddress([]byte{1}): newWrappedPrecompiledContract(&ecrecover{}),
+	common.BytesToAddress([]byte{2}): newWrappedPrecompiledContract(&sha256hash{}),
+	common.BytesToAddress([]byte{3}): newWrappedPrecompiledContract(&ripemd160hash{}),
+	common.BytesToAddress([]byte{4}): newWrappedPrecompiledContract(&dataCopy{}),
 }
 
 // PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum
 // contracts used in the Byzantium release.
-var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
-	common.BytesToAddress([]byte{1}): &ecrecover{},
-	common.BytesToAddress([]byte{2}): &sha256hash{},
-	common.BytesToAddress([]byte{3}): &ripemd160hash{},
-	common.BytesToAddress([]byte{4}): &dataCopy{},
-	common.BytesToAddress([]byte{5}): &bigModExp{},
-	common.BytesToAddress([]byte{6}): &bn256AddByzantium{},
-	common.BytesToAddress([]byte{7}): &bn256ScalarMulByzantium{},
-	common.BytesToAddress([]byte{8}): &bn256PairingByzantium{},
+var PrecompiledContractsByzantium = map[common.Address]StatefulPrecompiledContract{
+	common.BytesToAddress([]byte{1}): newWrappedPrecompiledContract(&ecrecover{}),
+	common.BytesToAddress([]byte{2}): newWrappedPrecompiledContract(&sha256hash{}),
+	common.BytesToAddress([]byte{3}): newWrappedPrecompiledContract(&ripemd160hash{}),
+	common.BytesToAddress([]byte{4}): newWrappedPrecompiledContract(&dataCopy{}),
+	common.BytesToAddress([]byte{5}): newWrappedPrecompiledContract(&bigModExp{}),
+	common.BytesToAddress([]byte{6}): newWrappedPrecompiledContract(&bn256AddByzantium{}),
+	common.BytesToAddress([]byte{7}): newWrappedPrecompiledContract(&bn256ScalarMulByzantium{}),
+	common.BytesToAddress([]byte{8}): newWrappedPrecompiledContract(&bn256PairingByzantium{}),
 }
 
 // PrecompiledContractsIstanbul contains the default set of pre-compiled Ethereum
 // contracts used in the Istanbul release.
-var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
-	common.BytesToAddress([]byte{1}): &ecrecover{},
-	common.BytesToAddress([]byte{2}): &sha256hash{},
-	common.BytesToAddress([]byte{3}): &ripemd160hash{},
-	common.BytesToAddress([]byte{4}): &dataCopy{},
-	common.BytesToAddress([]byte{5}): &bigModExp{},
-	common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
-	common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
-	common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
-	common.BytesToAddress([]byte{9}): &blake2F{},
+var PrecompiledContractsIstanbul = map[common.Address]StatefulPrecompiledContract{
+	common.BytesToAddress([]byte{1}): newWrappedPrecompiledContract(&ecrecover{}),
+	common.BytesToAddress([]byte{2}): newWrappedPrecompiledContract(&sha256hash{}),
+	common.BytesToAddress([]byte{3}): newWrappedPrecompiledContract(&ripemd160hash{}),
+	common.BytesToAddress([]byte{4}): newWrappedPrecompiledContract(&dataCopy{}),
+	common.BytesToAddress([]byte{5}): newWrappedPrecompiledContract(&bigModExp{}),
+	common.BytesToAddress([]byte{6}): newWrappedPrecompiledContract(&bn256AddIstanbul{}),
+	common.BytesToAddress([]byte{7}): newWrappedPrecompiledContract(&bn256ScalarMulIstanbul{}),
+	common.BytesToAddress([]byte{8}): newWrappedPrecompiledContract(&bn256PairingIstanbul{}),
+	common.BytesToAddress([]byte{9}): newWrappedPrecompiledContract(&blake2F{}),
 }
 
 // PrecompiledContractsYoloV1 contains the default set of pre-compiled Ethereum
 // contracts used in the Yolo v1 test release.
-var PrecompiledContractsYoloV1 = map[common.Address]PrecompiledContract{
-	common.BytesToAddress([]byte{1}):  &ecrecover{},
-	common.BytesToAddress([]byte{2}):  &sha256hash{},
-	common.BytesToAddress([]byte{3}):  &ripemd160hash{},
-	common.BytesToAddress([]byte{4}):  &dataCopy{},
-	common.BytesToAddress([]byte{5}):  &bigModExp{},
-	common.BytesToAddress([]byte{6}):  &bn256AddIstanbul{},
-	common.BytesToAddress([]byte{7}):  &bn256ScalarMulIstanbul{},
-	common.BytesToAddress([]byte{8}):  &bn256PairingIstanbul{},
-	common.BytesToAddress([]byte{9}):  &blake2F{},
-	common.BytesToAddress([]byte{10}): &bls12381G1Add{},
-	common.BytesToAddress([]byte{11}): &bls12381G1Mul{},
-	common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{},
-	common.BytesToAddress([]byte{13}): &bls12381G2Add{},
-	common.BytesToAddress([]byte{14}): &bls12381G2Mul{},
-	common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{},
-	common.BytesToAddress([]byte{16}): &bls12381Pairing{},
-	common.BytesToAddress([]byte{17}): &bls12381MapG1{},
-	common.BytesToAddress([]byte{18}): &bls12381MapG2{},
+var PrecompiledContractsYoloV1 = map[common.Address]StatefulPrecompiledContract{
+	common.BytesToAddress([]byte{1}):  newWrappedPrecompiledContract(&ecrecover{}),
+	common.BytesToAddress([]byte{2}):  newWrappedPrecompiledContract(&sha256hash{}),
+	common.BytesToAddress([]byte{3}):  newWrappedPrecompiledContract(&ripemd160hash{}),
+	common.BytesToAddress([]byte{4}):  newWrappedPrecompiledContract(&dataCopy{}),
+	common.BytesToAddress([]byte{5}):  newWrappedPrecompiledContract(&bigModExp{}),
+	common.BytesToAddress([]byte{6}):  newWrappedPrecompiledContract(&bn256AddIstanbul{}),
+	common.BytesToAddress([]byte{7}):  newWrappedPrecompiledContract(&bn256ScalarMulIstanbul{}),
+	common.BytesToAddress([]byte{8}):  newWrappedPrecompiledContract(&bn256PairingIstanbul{}),
+	common.BytesToAddress([]byte{9}):  newWrappedPrecompiledContract(&blake2F{}),
+	common.BytesToAddress([]byte{10}): newWrappedPrecompiledContract(&bls12381G1Add{}),
+	common.BytesToAddress([]byte{11}): newWrappedPrecompiledContract(&bls12381G1Mul{}),
+	common.BytesToAddress([]byte{12}): newWrappedPrecompiledContract(&bls12381G1MultiExp{}),
+	common.BytesToAddress([]byte{13}): newWrappedPrecompiledContract(&bls12381G2Add{}),
+	common.BytesToAddress([]byte{14}): newWrappedPrecompiledContract(&bls12381G2Mul{}),
+	common.BytesToAddress([]byte{15}): newWrappedPrecompiledContract(&bls12381G2MultiExp{}),
+	common.BytesToAddress([]byte{16}): newWrappedPrecompiledContract(&bls12381Pairing{}),
+	common.BytesToAddress([]byte{17}): newWrappedPrecompiledContract(&bls12381MapG1{}),
+	common.BytesToAddress([]byte{18}): newWrappedPrecompiledContract(&bls12381MapG2{}),
 }
 
 // RunPrecompiledContract runs and evaluates the output of a precompiled contract.
diff --git a/core/vm/contracts_stateful.go b/core/vm/contracts_stateful.go
new file mode 100644
index 0000000..3ef7a9e
--- /dev/null
+++ b/core/vm/contracts_stateful.go
@@ -0,0 +1,165 @@
+package vm
+
+import (
+	"errors"
+	"fmt"
+	"math/big"
+
+	"github.com/ava-labs/coreth/params"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/log"
+	"github.com/holiman/uint256"
+)
+
+// PrecompiledContractsApricot contains the default set of pre-compiled Ethereum
+// contracts used in the Istanbul release and the stateful precompiled contracts
+// added for the Avalanche Apricot release.
+// Apricot is incompatible with the YoloV1 Release since it does not include the
+// BLS12-381 Curve Operations added to the set of precompiled contracts
+var PrecompiledContractsApricot = map[common.Address]StatefulPrecompiledContract{
+	common.BytesToAddress([]byte{1}):                                  newWrappedPrecompiledContract(&ecrecover{}),
+	common.BytesToAddress([]byte{2}):                                  newWrappedPrecompiledContract(&sha256hash{}),
+	common.BytesToAddress([]byte{3}):                                  newWrappedPrecompiledContract(&ripemd160hash{}),
+	common.BytesToAddress([]byte{4}):                                  newWrappedPrecompiledContract(&dataCopy{}),
+	common.BytesToAddress([]byte{5}):                                  newWrappedPrecompiledContract(&bigModExp{}),
+	common.BytesToAddress([]byte{6}):                                  newWrappedPrecompiledContract(&bn256AddIstanbul{}),
+	common.BytesToAddress([]byte{7}):                                  newWrappedPrecompiledContract(&bn256ScalarMulIstanbul{}),
+	common.BytesToAddress([]byte{8}):                                  newWrappedPrecompiledContract(&bn256PairingIstanbul{}),
+	common.BytesToAddress([]byte{9}):                                  newWrappedPrecompiledContract(&blake2F{}),
+	common.HexToAddress("0x0100000000000000000000000000000000000000"): &deprecatedContract{msg: "hardcoded genesis contract has been deprecated"},
+	common.HexToAddress("0xffffffffffffffffffffffffffffffffffffff"):   &nativeAssetBalance{gasCost: params.AssetBalanceApricot},
+	common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffe"):   &nativeAssetCall{gasCost: params.AssetCallApricot},
+}
+
+// StatefulPrecompiledContract is the interface for executing a precompiled contract
+// This wraps the PrecompiledContracts native to Ethereum and allows adding in stateful
+// precompiled contracts to support native Avalanche asset transfers.
+type StatefulPrecompiledContract interface {
+	// Run executes a precompiled contract in the current state
+	// assumes that it has already been verified that [caller] can
+	// transfer [value].
+	Run(evm *EVM, caller ContractRef, addr common.Address, value *big.Int, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error)
+}
+
+// wrappedPrecompiledContract implements StatefulPrecompiledContract by wrapping stateless native precompiled contracts
+// in Ethereum.
+type wrappedPrecompiledContract struct {
+	p PrecompiledContract
+}
+
+func newWrappedPrecompiledContract(p PrecompiledContract) StatefulPrecompiledContract {
+	return &wrappedPrecompiledContract{p: p}
+}
+
+// Run ...
+func (w *wrappedPrecompiledContract) Run(evm *EVM, caller ContractRef, addr common.Address, value *big.Int, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
+	evm.Transfer(evm.StateDB, caller.Address(), addr, value)
+	return RunPrecompiledContract(w.p, input, suppliedGas)
+}
+
+// nativeAssetBalance is a precompiled contract used to retrieve the native asset balance
+type nativeAssetBalance struct {
+	gasCost uint64
+}
+
+// Run implements StatefulPrecompiledContract
+func (b *nativeAssetBalance) Run(evm *EVM, caller ContractRef, addr common.Address, value *big.Int, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
+	// input: encodePacked(address 20 bytes, assetID 32 bytes)
+	if value.Sign() != 0 {
+		return nil, suppliedGas, errors.New("cannot transfer value to native asset balance pre-compiled contract")
+	}
+
+	if suppliedGas < b.gasCost {
+		return nil, 0, ErrOutOfGas
+	}
+	remainingGas = suppliedGas - b.gasCost
+
+	if len(input) != 52 {
+		return nil, remainingGas, fmt.Errorf("input to native asset balance must be 52 bytes, containing address [20 bytes] and assetID [32 bytes], but found length: %d", len(input))
+	}
+	address := common.BytesToAddress(input[:20])
+	assetID := new(common.Hash)
+	assetID.SetBytes(input[20:52])
+
+	res, overflow := uint256.FromBig(evm.StateDB.GetBalanceMultiCoin(address, *assetID))
+	log.Info("nativeAssetBalance", "address", address, "assetID", assetID.Hex(), "res", res, "overflow", overflow)
+	if overflow {
+		return nil, remainingGas, errors.New("balance overflow")
+	}
+	return common.LeftPadBytes(res.Bytes(), 32), remainingGas, nil
+}
+
+// nativeAssetCall atomically transfers a native asset to a recipient address as well as calling that
+// address
+type nativeAssetCall struct {
+	gasCost uint64
+}
+
+// Run implements StatefulPrecompiledContract
+func (c *nativeAssetCall) Run(evm *EVM, caller ContractRef, addr common.Address, value *big.Int, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
+	// input: encodePacked(address 20 bytes, assetID 32 bytes, assetAmount 32 bytes, callData variable length bytes)
+	if suppliedGas < c.gasCost {
+		return nil, 0, ErrOutOfGas
+	}
+	remainingGas = suppliedGas - c.gasCost
+
+	if readOnly {
+		return nil, remainingGas, errors.New("cannot execute native asset transfer within read only call")
+	}
+
+	if len(input) < 84 {
+		return nil, remainingGas, fmt.Errorf("missing inputs to native asset transfer, expected input of length at least 84, but found %d", len(input))
+	}
+	to := common.BytesToAddress(input[:20])
+	assetID := new(common.Hash)
+	assetID.SetBytes(input[20:52])
+	assetAmount := new(big.Int).SetBytes(input[52:84])
+	callData := input[84:]
+	log.Info("nativeAssetCall", "input", fmt.Sprintf("0x%x", input), "to", to.Hex(), "assetID", assetID.Hex(), "assetAmount", assetAmount, "callData", fmt.Sprintf("0x%x", callData))
+
+	mcerr := evm.Context.CanTransferMC(evm.StateDB, caller.Address(), to, assetID, assetAmount)
+	if mcerr == 1 {
+		log.Info("Insufficient balance for mc transfer")
+		return nil, remainingGas, ErrInsufficientBalance
+	} else if mcerr != 0 {
+		log.Info("incompatible account for mc transfer")
+		return nil, remainingGas, ErrIncompatibleAccount
+	}
+
+	snapshot := evm.StateDB.Snapshot()
+
+	if !evm.StateDB.Exist(to) {
+		log.Info("Creating account entry", "address", to.Hex())
+		remainingGas -= params.CallNewAccountGas
+		evm.StateDB.CreateAccount(to)
+	}
+
+	// Send [value] to [to] address
+	evm.Transfer(evm.StateDB, caller.Address(), to, value)
+	evm.TransferMultiCoin(evm.StateDB, caller.Address(), to, assetID, assetAmount)
+	ret, remainingGas, err = evm.Call(caller, to, callData, remainingGas, big.NewInt(0))
+	log.Info("Finished TransferMultiCoin and call", "return", ret, "remainingGas", remainingGas, "error", err)
+
+	// When an error was returned by the EVM or when setting the creation code
+	// above we revert to the snapshot and consume any gas remaining. Additionally
+	// when we're in homestead this also counts for code storage gas errors.
+	if err != nil {
+		evm.StateDB.RevertToSnapshot(snapshot)
+		if err != ErrExecutionReverted {
+			remainingGas = 0
+		}
+		// TODO: consider clearing up unused snapshots:
+		//} else {
+		//	evm.StateDB.DiscardSnapshot(snapshot)
+	}
+	return ret, remainingGas, err
+}
+
+type deprecatedContract struct {
+	msg string
+}
+
+// Run ...
+func (d *deprecatedContract) Run(evm *EVM, caller ContractRef, addr common.Address, value *big.Int, input []byte, suppliedGas uint64, readOnly bool) (ret []byte, remainingGas uint64, err error) {
+	return nil, suppliedGas, errors.New(d.msg)
+}
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 54c9c7f..ab04790 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -34,19 +34,23 @@ var emptyCodeHash = crypto.Keccak256Hash(nil)
 
 type (
 	// CanTransferFunc is the signature of a transfer guard function
-	CanTransferFunc   func(StateDB, common.Address, *big.Int) bool
+	CanTransferFunc func(StateDB, common.Address, *big.Int) bool
+	// CanTransferMCFunc is the signature of a transfer native asset function
 	CanTransferMCFunc func(StateDB, common.Address, common.Address, *common.Hash, *big.Int) int
 	// TransferFunc is the signature of a transfer function
-	TransferFunc   func(StateDB, common.Address, common.Address, *big.Int)
+	TransferFunc func(StateDB, common.Address, common.Address, *big.Int)
+	// TransferMCFunc is the signature of a transfer native asset function
 	TransferMCFunc func(StateDB, common.Address, common.Address, *common.Hash, *big.Int)
 	// GetHashFunc returns the n'th block hash in the blockchain
 	// and is used by the BLOCKHASH EVM op code.
 	GetHashFunc func(uint64) common.Hash
 )
 
-func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
-	var precompiles map[common.Address]PrecompiledContract
+func (evm *EVM) precompile(addr common.Address) (StatefulPrecompiledContract, bool) {
+	var precompiles map[common.Address]StatefulPrecompiledContract
 	switch {
+	case evm.chainRules.IsApricot:
+		precompiles = PrecompiledContractsApricot
 	case evm.chainRules.IsYoloV1:
 		precompiles = PrecompiledContractsYoloV1
 	case evm.chainRules.IsIstanbul:
@@ -222,7 +226,6 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
 		}
 		evm.StateDB.CreateAccount(addr)
 	}
-	evm.Transfer(evm.StateDB, caller.Address(), addr, value)
 
 	// Capture the tracer start/end events in debug mode
 	if evm.vmConfig.Debug && evm.depth == 0 {
@@ -233,8 +236,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
 	}
 
 	if isPrecompile {
-		ret, gas, err = RunPrecompiledContract(p, input, gas)
+		ret, gas, err = p.Run(evm, caller, addr, value, input, gas, false)
 	} else {
+		evm.Transfer(evm.StateDB, caller.Address(), addr, value)
 		// Initialise a new contract and set the code that is to be used by the EVM.
 		// The contract is a scoped environment for this execution context only.
 		code := evm.StateDB.GetCode(addr)
@@ -371,7 +375,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
 
 	// It is allowed to call precompiles, even via delegatecall
 	if p, isPrecompile := evm.precompile(addr); isPrecompile {
-		ret, gas, err = RunPrecompiledContract(p, input, gas)
+		ret, gas, err = p.Run(evm, caller, addr, value, input, gas, false)
 	} else {
 		addrCopy := addr
 		// Initialise a new contract and set the code that is to be used by the EVM.
@@ -407,7 +411,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
 
 	// It is allowed to call precompiles, even via delegatecall
 	if p, isPrecompile := evm.precompile(addr); isPrecompile {
-		ret, gas, err = RunPrecompiledContract(p, input, gas)
+		ret, gas, err = p.Run(evm, caller, addr, big0, input, gas, false)
 	} else {
 		addrCopy := addr
 		// Initialise a new contract and make initialise the delegate values
@@ -451,7 +455,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
 	evm.StateDB.AddBalance(addr, big0)
 
 	if p, isPrecompile := evm.precompile(addr); isPrecompile {
-		ret, gas, err = RunPrecompiledContract(p, input, gas)
+		ret, gas, err = p.Run(evm, caller, addr, big0, input, gas, true)
 	} else {
 		// At this point, we use a copy of address. If we don't, the go compiler will
 		// leak the 'contract' to the outer scope, and make allocation for 'contract'
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index a114a8d..1292650 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -26,6 +26,7 @@ import (
 )
 
 var (
+	// BuiltinAddr ...
 	BuiltinAddr = common.Address{
 		1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -106,6 +107,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
 	if cfg.JumpTable[STOP] == nil {
 		var jt JumpTable
 		switch {
+		case evm.chainRules.IsApricot:
+			jt = apricotInstructionSet
 		case evm.chainRules.IsYoloV1:
 			jt = yoloV1InstructionSet
 		case evm.chainRules.IsIstanbul:
@@ -146,6 +149,15 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
 // considered a revert-and-consume-all-gas operation except for
 // ErrExecutionReverted which means revert-and-keep-gas-left.
 func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
+	// TODO remove after Apricot network upgrade.
+	// [BuiltinAddr] is overridden by a precompile in Apricot.
+	// This makes any call to [BuiltinAddr] a no-op once the network
+	// upgrade occurs. Then in the upgrade after the hard fork, this
+	// code can be safely removed.
+	// This can't occur before the hard fork because it would change
+	// the behavior of upgraded nodes before the hard fork occurs
+	// since there is no mechanism to change this code based on the
+	// block height.
 	if contract.Address() == BuiltinAddr {
 		self := AccountRef(contract.Caller())
 		if _, ok := contract.caller.(*Contract); ok {
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 9b538d4..fc12f93 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -49,6 +49,7 @@ type operation struct {
 }
 
 var (
+	apricotInstructionSet          = newApricotInstructionSet()
 	frontierInstructionSet         = newFrontierInstructionSet()
 	homesteadInstructionSet        = newHomesteadInstructionSet()
 	tangerineWhistleInstructionSet = newTangerineWhistleInstructionSet()
@@ -1043,3 +1044,13 @@ func newFrontierInstructionSet() JumpTable {
 		},
 	}
 }
+
+// newApricotInstructionSet returns a new instruction set
+// compatible with the Apricot release, which deprecates
+// added instructions: CALLEX and BALANCEMC
+func newApricotInstructionSet() JumpTable {
+	instructionSet := newFrontierInstructionSet()
+	instructionSet[CALLEX] = nil
+	instructionSet[BALANCEMC] = nil
+	return instructionSet
+}
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index c5097f8..cc9518e 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -208,6 +208,7 @@ const (
 	SWAP
 )
 
+// Constants supporting native asset operations
 const (
 	BALANCEMC = 0xcd
 	//EMC       = 0xce
diff --git a/eth/backend.go b/eth/backend.go
index 9fb8f38..e26cbb3 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -28,7 +28,6 @@ import (
 	"github.com/ava-labs/coreth/consensus"
 	"github.com/ava-labs/coreth/consensus/clique"
 	"github.com/ava-labs/coreth/consensus/dummy"
-	"github.com/ava-labs/coreth/consensus/ethash"
 	"github.com/ava-labs/coreth/core"
 	"github.com/ava-labs/coreth/core/bloombits"
 	"github.com/ava-labs/coreth/core/rawdb"
@@ -152,7 +151,7 @@ func New(stack *node.Node, config *Config,
 		chainDb:           chainDb,
 		eventMux:          stack.EventMux(),
 		accountManager:    stack.AccountManager(),
-		engine:            CreateConsensusEngine(stack, chainConfig, &config.Ethash, config.Miner.Notify, config.Miner.Noverify, chainDb, cb),
+		engine:            CreateConsensusEngine(cb),
 		closeBloomHandler: make(chan struct{}),
 		networkID:         config.NetworkId,
 		gasPrice:          config.Miner.GasPrice,
@@ -265,7 +264,7 @@ func makeExtraData(extra []byte) []byte {
 }
 
 // CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service
-func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database, cb *dummy.ConsensusCallbacks) consensus.Engine {
+func CreateConsensusEngine(cb *dummy.ConsensusCallbacks) consensus.Engine {
 	return dummy.NewDummyEngine(cb)
 }
 
diff --git a/examples/arc20/main.go b/examples/arc20/main.go
new file mode 100644
index 0000000..6bac290
--- /dev/null
+++ b/examples/arc20/main.go
@@ -0,0 +1,93 @@
+package main
+
+import (
+	"context"
+	"crypto/ecdsa"
+	"fmt"
+	"math/big"
+
+	"github.com/ava-labs/coreth/params"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/ethereum/go-ethereum/ethclient"
+)
+
+var (
+	uri                    = "http://127.0.0.1:9650/ext/bc/C/rpc"
+	nativeAssetCallAddr    = common.BytesToAddress([]byte{11})
+	nativeAssetBalanceAddr = common.BytesToAddress([]byte{10})
+	chainID                = new(big.Int).SetUint64(43112)
+	privateKey             *ecdsa.PrivateKey
+	address                common.Address
+	erc20                  common.Address
+	assetID                *big.Int
+	amount                 *big.Int
+	gasLimit               = uint64(700000)
+	gasPrice               = params.MinGasPrice
+)
+
+func init() {
+	pk, err := crypto.HexToECDSA("da777cd656c8760a7d378ae04d7dd0cd7a703c450c84e6c2faa886ca97517df7")
+	if err != nil {
+		panic(err)
+	}
+	privateKey = pk
+	address = crypto.PubkeyToAddress(privateKey.PublicKey)
+	erc20 = common.HexToAddress("0xea75d59faF258F1fdf2b94F158e54D7ad44359B6")
+	aID, success := new(big.Int).SetString("21556002304173297996004673107070813226981923092188601867247360128943231977558", 10)
+	if !success {
+		panic("Failed to read assetID")
+	}
+	assetID = aID
+	amount = new(big.Int).SetUint64(100)
+}
+
+// createDepositCallData creates the callData argument to nativeAssetTransfer to move [amount]
+// of [assetID] to [erc20] address and call the deposit function with signature "deposit()"
+func createDepositCallData(erc20 common.Address, assetID, amount *big.Int) []byte {
+	// erc20 addr, assetID, assetAmount, callData
+	signatureHash := crypto.Keccak256([]byte("deposit()"))
+	fmt.Printf("signatureHash: 0x%x\n", signatureHash)
+	functionSignature := signatureHash[:4]
+	data := make([]byte, 0, 84)
+	data = append(data, erc20.Bytes()...)
+	data = append(data, common.LeftPadBytes(assetID.Bytes(), 32)...)
+	data = append(data, common.LeftPadBytes(amount.Bytes(), 32)...)
+	data = append(data, functionSignature...) // Add this back in to trigger call to deposit
+	fmt.Printf("deposit callData: 0x%x\n", data)
+	return data
+}
+
+// createDepositTransaction creates a transaction to deposit native asset funds in [erc20]
+func createDepositTransaction(nonce uint64, erc20 common.Address, assetID, amount *big.Int, gasLimit uint64, gasPrice *big.Int) *types.Transaction {
+	callData := createDepositCallData(erc20, assetID, amount)
+	return types.NewTransaction(nonce, nativeAssetCallAddr, new(big.Int), gasLimit, gasPrice, callData)
+}
+
+func main() {
+	client, err := ethclient.Dial(uri)
+	if err != nil {
+		panic(err)
+	}
+	ctx := context.Background()
+	nonce, err := client.NonceAt(ctx, address, nil)
+	if err != nil {
+		panic(err)
+	}
+	fmt.Printf("Creating deposit transaction from: %s, erc20 address: %s, assetID: %d, amount: %d, nonce: %d\n", address.Hex(), erc20.Hex(), assetID, amount, nonce)
+	// Create and sign deposit transaction from account that has been funded with sufficient AVAX to
+	// pay gas costs and sufficient amount of the native asset to make the deposit
+	tx := createDepositTransaction(nonce, erc20, assetID, amount, gasLimit, gasPrice)
+	signer := types.NewEIP155Signer(chainID)
+	signedTx, err := types.SignTx(tx, signer, privateKey)
+	if err != nil {
+		panic(err)
+	}
+	// Send the signed transaction to the client
+	if err := client.SendTransaction(ctx, signedTx); err != nil {
+		panic(err)
+	}
+	txHash := signedTx.Hash()
+	fmt.Printf("txHash: %s\n", txHash.Hex())
+}
diff --git a/examples/multicoin/main.go b/examples/multicoin/main.go
index 35158a8..c2257e3 100644
--- a/examples/multicoin/main.go
+++ b/examples/multicoin/main.go
@@ -5,6 +5,15 @@ import (
 	"encoding/json"
 	"flag"
 	"fmt"
+	"go/build"
+	"math/big"
+	"os"
+	"os/signal"
+	"path/filepath"
+	"strings"
+	"syscall"
+	"time"
+
 	"github.com/ava-labs/coreth"
 	"github.com/ava-labs/coreth/core"
 	"github.com/ava-labs/coreth/core/types"
@@ -18,14 +27,6 @@ import (
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/rlp"
-	"go/build"
-	"math/big"
-	"os"
-	"os/signal"
-	"path/filepath"
-	"strings"
-	"syscall"
-	"time"
 )
 
 func checkError(err error) {
diff --git a/params/config.go b/params/config.go
index e5ec64b..1e14c40 100644
--- a/params/config.go
+++ b/params/config.go
@@ -53,6 +53,49 @@ var CheckpointOracles = map[common.Hash]*CheckpointOracleConfig{
 }
 
 var (
+	// AvalancheMainnetChainID ...
+	AvalancheMainnetChainID = big.NewInt(43114)
+	// AvalancheFujiChainID ...
+	AvalancheFujiChainID = big.NewInt(43113)
+)
+
+var (
+	// AvalancheApricotMainnetChainConfig is the configuration for Avalanche Main Network
+	AvalancheApricotMainnetChainConfig = &ChainConfig{
+		ChainID:             AvalancheMainnetChainID,
+		HomesteadBlock:      big.NewInt(0),
+		DAOForkBlock:        big.NewInt(0),
+		DAOForkSupport:      true,
+		EIP150Block:         big.NewInt(0),
+		EIP150Hash:          common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
+		EIP155Block:         big.NewInt(0),
+		ByzantiumBlock:      big.NewInt(0),
+		ConstantinopleBlock: big.NewInt(0),
+		PetersburgBlock:     big.NewInt(0),
+		IstanbulBlock:       big.NewInt(0),
+		MuirGlacierBlock:    big.NewInt(0),
+		ApricotBlock:        big.NewInt(0), // TODO specify correct block height
+	}
+
+	// AvalancheApricotFujiChainConfig is the configuration for the Fuji Test Network
+	AvalancheApricotFujiChainConfig = &ChainConfig{
+		ChainID:             AvalancheFujiChainID,
+		HomesteadBlock:      big.NewInt(0),
+		DAOForkBlock:        big.NewInt(0),
+		DAOForkSupport:      true,
+		EIP150Block:         big.NewInt(0),
+		EIP150Hash:          common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
+		EIP155Block:         big.NewInt(0),
+		ByzantiumBlock:      big.NewInt(0),
+		ConstantinopleBlock: big.NewInt(0),
+		PetersburgBlock:     big.NewInt(0),
+		IstanbulBlock:       big.NewInt(0),
+		MuirGlacierBlock:    big.NewInt(0),
+		ApricotBlock:        big.NewInt(0), // TODO specify correct block height
+	}
+
+	// TODO update local network genesis to enable Apricot
+
 	// MainnetChainConfig is the chain parameters to run a node on the main network.
 	MainnetChainConfig = &ChainConfig{
 		ChainID:             big.NewInt(1),
@@ -239,16 +282,16 @@ var (
 	//
 	// This configuration is intentionally not using keyed fields to force anyone
 	// adding flags to the config to also have to set these fields.
-	AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil}
+	AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, new(EthashConfig), nil}
 
 	// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
 	// and accepted by the Ethereum core developers into the Clique consensus.
 	//
 	// This configuration is intentionally not using keyed fields to force anyone
 	// adding flags to the config to also have to set these fields.
-	AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
+	AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
 
-	TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, new(EthashConfig), nil}
+	TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, new(EthashConfig), nil}
 	TestRules       = TestChainConfig.Rules(new(big.Int))
 )
 
@@ -323,6 +366,8 @@ type ChainConfig struct {
 	YoloV1Block *big.Int `json:"yoloV1Block,omitempty"` // YOLO v1: https://github.com/ethereum/EIPs/pull/2657 (Ephemeral testnet)
 	EWASMBlock  *big.Int `json:"ewasmBlock,omitempty"`  // EWASM switch block (nil = no fork, 0 = already activated)
 
+	ApricotBlock *big.Int `json:"apricotBlock,omitempty"` // Apricot switch block (nil = no fork, 0 = already activated)
+
 	// Various consensus engines
 	Ethash *EthashConfig `json:"ethash,omitempty"`
 	Clique *CliqueConfig `json:"clique,omitempty"`
@@ -358,7 +403,7 @@ func (c *ChainConfig) String() string {
 	default:
 		engine = "unknown"
 	}
-	return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, YOLO v1: %v, Engine: %v}",
+	return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, YOLO v1: %v, Apricot: %v, Engine: %v}",
 		c.ChainID,
 		c.HomesteadBlock,
 		c.DAOForkBlock,
@@ -372,6 +417,7 @@ func (c *ChainConfig) String() string {
 		c.IstanbulBlock,
 		c.MuirGlacierBlock,
 		c.YoloV1Block,
+		c.ApricotBlock,
 		engine,
 	)
 }
@@ -438,6 +484,12 @@ func (c *ChainConfig) IsEWASM(num *big.Int) bool {
 	return isForked(c.EWASMBlock, num)
 }
 
+// IsApricot returns whether num represents a block number after the Apricot fork
+func (c *ChainConfig) IsApricot(num *big.Int) bool {
+	return isForked(c.ApricotBlock, num)
+}
+
+// TODO review how this works and see if it will work for a live transition
 // CheckCompatible checks whether scheduled fork transitions have been imported
 // with a mismatching chain configuration.
 func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *ConfigCompatError {
@@ -477,6 +529,10 @@ func (c *ChainConfig) CheckConfigForkOrder() error {
 		{name: "istanbulBlock", block: c.IstanbulBlock},
 		{name: "muirGlacierBlock", block: c.MuirGlacierBlock, optional: true},
 		{name: "yoloV1Block", block: c.YoloV1Block},
+		// TODO figure out how Apricot fits in here.
+		// Precompiled contracts start from other end so they are compatible
+		// but it may be incompatible (and it was before Apricot as well) due
+		// to how the instruction sets are created
 	} {
 		if lastFork.name != "" {
 			// Next one must be higher number
@@ -610,6 +666,8 @@ type Rules struct {
 	IsHomestead, IsEIP150, IsEIP155, IsEIP158               bool
 	IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
 	IsYoloV1                                                bool
+	// Avalanche Releases
+	IsApricot bool
 }
 
 // Rules ensures c's ChainID is not nil.
@@ -629,5 +687,6 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
 		IsPetersburg:     c.IsPetersburg(num),
 		IsIstanbul:       c.IsIstanbul(num),
 		IsYoloV1:         c.IsYoloV1(num),
+		IsApricot:        c.IsApricot(num),
 	}
 }
diff --git a/params/protocol_params.go b/params/protocol_params.go
index 3536c83..fdfb9a5 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -95,6 +95,8 @@ const (
 	ExtcodeHashGasConstantinople uint64 = 400  // Cost of EXTCODEHASH (introduced in Constantinople)
 	ExtcodeHashGasEIP1884        uint64 = 700  // Cost of EXTCODEHASH after EIP 1884 (part in Istanbul)
 	SelfdestructGasEIP150        uint64 = 5000 // Cost of SELFDESTRUCT post EIP 150 (Tangerine)
+	AssetBalanceApricot          uint64 = 20   // Cost of calling PrecompiledContract AssetBalance (Apricot)
+	AssetCallApricot             uint64 = 40   // Cost of calling PrecompiledContract AssetCall (Apricot)
 
 	// EXP has a dynamic portion depending on the size of the exponent
 	ExpByteFrontier uint64 = 10 // was set to 10 in Frontier
diff --git a/plugin/evm/export_tx.go b/plugin/evm/export_tx.go
index 2735573..2ebfeff 100644
--- a/plugin/evm/export_tx.go
+++ b/plugin/evm/export_tx.go
@@ -240,8 +240,8 @@ func (vm *VM) newExportTx(
 func (tx *UnsignedExportTx) EVMStateTransfer(vm *VM, state *state.StateDB) error {
 	addrs := map[[20]byte]uint64{}
 	for _, from := range tx.Ins {
-		log.Info("crosschain C->X", "addr", from.Address, "amount", from.Amount)
 		if from.AssetID == vm.ctx.AVAXAssetID {
+			log.Info("crosschain C->X", "from", from.Address, "amount", from.Amount, "assetID", "AVAX")
 			amount := new(big.Int).Mul(
 				new(big.Int).SetUint64(from.Amount), x2cRate)
 			if state.GetBalance(from.Address).Cmp(amount) < 0 {
@@ -250,6 +250,7 @@ func (tx *UnsignedExportTx) EVMStateTransfer(vm *VM, state *state.StateDB) error
 			state.SubBalance(from.Address, amount)
 		} else {
 			amount := new(big.Int).SetUint64(from.Amount)
+			log.Info("crosschain C->X", "from", from.Address, "amount", from.Amount, "assetID", from.AssetID)
 			if state.GetBalanceMultiCoin(from.Address, common.Hash(from.AssetID)).Cmp(amount) < 0 {
 				return errInsufficientFunds
 			}
diff --git a/plugin/evm/import_tx.go b/plugin/evm/import_tx.go
index 1ec394c..d6b4b7d 100644
--- a/plugin/evm/import_tx.go
+++ b/plugin/evm/import_tx.go
@@ -270,12 +270,13 @@ func (vm *VM) newImportTx(
 // accounts accordingly with the imported EVMOutputs
 func (tx *UnsignedImportTx) EVMStateTransfer(vm *VM, state *state.StateDB) error {
 	for _, to := range tx.Outs {
-		log.Info("crosschain X->C", "addr", to.Address, "amount", to.Amount)
 		if to.AssetID == vm.ctx.AVAXAssetID {
+			log.Info("crosschain X->C", "to", to.Address, "amount", to.Amount, "assetID", "AVAX")
 			amount := new(big.Int).Mul(
 				new(big.Int).SetUint64(to.Amount), x2cRate)
 			state.AddBalance(to.Address, amount)
 		} else {
+			log.Info("crosschain X->C", "to", to.Address, "amount", to.Amount, "assetID", to.AssetID)
 			amount := new(big.Int).SetUint64(to.Amount)
 			state.AddBalanceMultiCoin(to.Address, common.Hash(to.AssetID), amount)
 		}
diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go
index c9bdad9..fc571b9 100644
--- a/plugin/evm/vm.go
+++ b/plugin/evm/vm.go
@@ -239,6 +239,15 @@ func (vm *VM) Initialize(
 		return err
 	}
 
+	// Set the Apricot Fork Block on the ChainConfig
+	// TODO switch to correct block number of the fork
+	switch {
+	case g.Config.ChainID.Cmp(params.AvalancheMainnetChainID) == 0:
+		g.Config.ApricotBlock = nil
+	case g.Config.ChainID.Cmp(params.AvalancheFujiChainID) == 0:
+		g.Config.ApricotBlock = nil
+	}
+
 	vm.acceptedDB = prefixdb.New([]byte(acceptedPrefix), db)
 
 	vm.chainID = g.Config.ChainID
-- 
cgit v1.2.3-70-g09d2


From 3bf6e094864fde72484b824749019c64411239c3 Mon Sep 17 00:00:00 2001
From: Aaron Buchwald <aaron.buchwald56@gmail.com>
Date: Tue, 15 Dec 2020 17:05:31 -0500
Subject: Improve examples script

---
 examples/arc20/main.go | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/examples/arc20/main.go b/examples/arc20/main.go
index 6bac290..25f66bf 100644
--- a/examples/arc20/main.go
+++ b/examples/arc20/main.go
@@ -15,8 +15,8 @@ import (
 
 var (
 	uri                    = "http://127.0.0.1:9650/ext/bc/C/rpc"
-	nativeAssetCallAddr    = common.BytesToAddress([]byte{11})
-	nativeAssetBalanceAddr = common.BytesToAddress([]byte{10})
+	nativeAssetBalanceAddr = common.HexToAddress("0x0100000000000000000000000000000000000001")
+	nativeAssetCallAddr    = common.HexToAddress("0x0100000000000000000000000000000000000002")
 	chainID                = new(big.Int).SetUint64(43112)
 	privateKey             *ecdsa.PrivateKey
 	address                common.Address
@@ -28,14 +28,23 @@ var (
 )
 
 func init() {
+	// Private Key to sign deposit transaction
+	// To export from MetaMask:
+	// 1) Click ellipsis on right hand side of account page
+	// 2) View Account Details
+	// 3) Export Private Key
 	pk, err := crypto.HexToECDSA("da777cd656c8760a7d378ae04d7dd0cd7a703c450c84e6c2faa886ca97517df7")
 	if err != nil {
 		panic(err)
 	}
 	privateKey = pk
+	// Extract From Address from the private key
 	address = crypto.PubkeyToAddress(privateKey.PublicKey)
-	erc20 = common.HexToAddress("0xea75d59faF258F1fdf2b94F158e54D7ad44359B6")
-	aID, success := new(big.Int).SetString("21556002304173297996004673107070813226981923092188601867247360128943231977558", 10)
+	// erc20 = common.HexToAddress("0xea75d59faF258F1fdf2b94F158e54D7ad44359B6")
+	// Address of ARC-20 to deposit funds in
+	erc20 = common.HexToAddress("0xC67353d203d748FC6D5E50bf44c7813C39EADea3")
+	// AssetID as a uint256 integer as displayed in Remix of the ARC-20
+	aID, success := new(big.Int).SetString("49381164258359268658146204715775898207620442222804199785202784815303309497754", 10)
 	if !success {
 		panic("Failed to read assetID")
 	}
-- 
cgit v1.2.3-70-g09d2


From 34da2a9bbc1cad12e5d1ab14799290e4b4987f99 Mon Sep 17 00:00:00 2001
From: Aaron Buchwald <aaron.buchwald56@gmail.com>
Date: Tue, 15 Dec 2020 17:05:51 -0500
Subject: Fix apricot instruction set

---
 core/vm/contracts_stateful.go |  4 ++--
 core/vm/jump_table.go         | 20 ++++++++++----------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/core/vm/contracts_stateful.go b/core/vm/contracts_stateful.go
index 3ef7a9e..b91acfb 100644
--- a/core/vm/contracts_stateful.go
+++ b/core/vm/contracts_stateful.go
@@ -27,8 +27,8 @@ var PrecompiledContractsApricot = map[common.Address]StatefulPrecompiledContract
 	common.BytesToAddress([]byte{8}):                                  newWrappedPrecompiledContract(&bn256PairingIstanbul{}),
 	common.BytesToAddress([]byte{9}):                                  newWrappedPrecompiledContract(&blake2F{}),
 	common.HexToAddress("0x0100000000000000000000000000000000000000"): &deprecatedContract{msg: "hardcoded genesis contract has been deprecated"},
-	common.HexToAddress("0xffffffffffffffffffffffffffffffffffffff"):   &nativeAssetBalance{gasCost: params.AssetBalanceApricot},
-	common.HexToAddress("0xfffffffffffffffffffffffffffffffffffffe"):   &nativeAssetCall{gasCost: params.AssetCallApricot},
+	common.HexToAddress("0x0100000000000000000000000000000000000001"): &nativeAssetBalance{gasCost: params.AssetBalanceApricot},
+	common.HexToAddress("0x0100000000000000000000000000000000000002"): &nativeAssetCall{gasCost: params.AssetCallApricot},
 }
 
 // StatefulPrecompiledContract is the interface for executing a precompiled contract
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index fc12f93..44e2ebc 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -63,6 +63,16 @@ var (
 // JumpTable contains the EVM opcodes supported at a given fork.
 type JumpTable [256]*operation
 
+// newApricotInstructionSet returns a new instruction set
+// compatible with the Apricot release, which deprecates
+// added instructions: CALLEX and BALANCEMC
+func newApricotInstructionSet() JumpTable {
+	instructionSet := newIstanbulInstructionSet()
+	instructionSet[CALLEX] = nil
+	instructionSet[BALANCEMC] = nil
+	return instructionSet
+}
+
 func newYoloV1InstructionSet() JumpTable {
 	instructionSet := newIstanbulInstructionSet()
 
@@ -1044,13 +1054,3 @@ func newFrontierInstructionSet() JumpTable {
 		},
 	}
 }
-
-// newApricotInstructionSet returns a new instruction set
-// compatible with the Apricot release, which deprecates
-// added instructions: CALLEX and BALANCEMC
-func newApricotInstructionSet() JumpTable {
-	instructionSet := newFrontierInstructionSet()
-	instructionSet[CALLEX] = nil
-	instructionSet[BALANCEMC] = nil
-	return instructionSet
-}
-- 
cgit v1.2.3-70-g09d2


From b892b31f7f9bc9a63eb9774e7fbe0b5cd82891ae Mon Sep 17 00:00:00 2001
From: Aaron Buchwald <aaron.buchwald56@gmail.com>
Date: Tue, 15 Dec 2020 17:06:05 -0500
Subject: Update precompile addresses

---
 contracts/nativeAssets.sol | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/contracts/nativeAssets.sol b/contracts/nativeAssets.sol
index 0656271..836c4d8 100644
--- a/contracts/nativeAssets.sol
+++ b/contracts/nativeAssets.sol
@@ -3,8 +3,8 @@
 pragma solidity >=0.6.0 <0.8.0;
 
 library NativeAssets {
-    address constant balanceAddr = 0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF;
-    address constant transferAddr = 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE;
+    address constant balanceAddr = 0x0100000000000000000000000000000000000001;
+    address constant transferAddr = 0x0100000000000000000000000000000000000002;
 
     function assetBalance(address addr, uint256 assetID) public returns (uint256) {
         (bool success, bytes memory data) = balanceAddr.call(abi.encodePacked(addr, assetID));
-- 
cgit v1.2.3-70-g09d2


From cc2a13f91f95bd377f5c3efb86ebbcc7096f6d97 Mon Sep 17 00:00:00 2001
From: Aaron Buchwald <aaron.buchwald56@gmail.com>
Date: Tue, 15 Dec 2020 17:30:03 -0500
Subject: Set chain rules based on block times for mainnet/fuji

---
 contracts/arc20.sol |  2 +-
 core/vm/evm.go      |  2 +-
 params/config.go    | 84 +++++++++++++++++++++++++++++++----------------------
 plugin/evm/vm.go    |  5 ++--
 4 files changed, 53 insertions(+), 40 deletions(-)

diff --git a/contracts/arc20.sol b/contracts/arc20.sol
index a7fc997..d1af6a3 100644
--- a/contracts/arc20.sol
+++ b/contracts/arc20.sol
@@ -2,7 +2,7 @@
 
 pragma solidity >=0.6.0 <0.8.0;
 
-import {NativeAssets} from "./NativeAssets.sol";
+import {NativeAssets} from "./nativeAssets.sol";
 
 contract ARC20 {
 
diff --git a/core/vm/evm.go b/core/vm/evm.go
index ab04790..fe840da 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -152,7 +152,7 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmCon
 		StateDB:      statedb,
 		vmConfig:     vmConfig,
 		chainConfig:  chainConfig,
-		chainRules:   chainConfig.Rules(ctx.BlockNumber),
+		chainRules:   chainConfig.AvalancheRules(ctx.BlockNumber, ctx.Time),
 		interpreters: make([]Interpreter, 0, 1),
 	}
 
diff --git a/params/config.go b/params/config.go
index 1e14c40..d5db94f 100644
--- a/params/config.go
+++ b/params/config.go
@@ -20,6 +20,7 @@ import (
 	"encoding/binary"
 	"fmt"
 	"math/big"
+	"time"
 
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/crypto"
@@ -62,40 +63,38 @@ var (
 var (
 	// AvalancheApricotMainnetChainConfig is the configuration for Avalanche Main Network
 	AvalancheApricotMainnetChainConfig = &ChainConfig{
-		ChainID:             AvalancheMainnetChainID,
-		HomesteadBlock:      big.NewInt(0),
-		DAOForkBlock:        big.NewInt(0),
-		DAOForkSupport:      true,
-		EIP150Block:         big.NewInt(0),
-		EIP150Hash:          common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
-		EIP155Block:         big.NewInt(0),
-		ByzantiumBlock:      big.NewInt(0),
-		ConstantinopleBlock: big.NewInt(0),
-		PetersburgBlock:     big.NewInt(0),
-		IstanbulBlock:       big.NewInt(0),
-		MuirGlacierBlock:    big.NewInt(0),
-		ApricotBlock:        big.NewInt(0), // TODO specify correct block height
+		ChainID:               AvalancheMainnetChainID,
+		HomesteadBlock:        big.NewInt(0),
+		DAOForkBlock:          big.NewInt(0),
+		DAOForkSupport:        true,
+		EIP150Block:           big.NewInt(0),
+		EIP150Hash:            common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
+		EIP155Block:           big.NewInt(0),
+		ByzantiumBlock:        big.NewInt(0),
+		ConstantinopleBlock:   big.NewInt(0),
+		PetersburgBlock:       big.NewInt(0),
+		IstanbulBlock:         big.NewInt(0),
+		MuirGlacierBlock:      big.NewInt(0),
+		ApricotBlockTimestamp: new(big.Int).SetUint64(uint64(time.Date(2021, 1, 7, 5, 00, 0, 0, time.UTC).Unix())),
 	}
 
 	// AvalancheApricotFujiChainConfig is the configuration for the Fuji Test Network
 	AvalancheApricotFujiChainConfig = &ChainConfig{
-		ChainID:             AvalancheFujiChainID,
-		HomesteadBlock:      big.NewInt(0),
-		DAOForkBlock:        big.NewInt(0),
-		DAOForkSupport:      true,
-		EIP150Block:         big.NewInt(0),
-		EIP150Hash:          common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
-		EIP155Block:         big.NewInt(0),
-		ByzantiumBlock:      big.NewInt(0),
-		ConstantinopleBlock: big.NewInt(0),
-		PetersburgBlock:     big.NewInt(0),
-		IstanbulBlock:       big.NewInt(0),
-		MuirGlacierBlock:    big.NewInt(0),
-		ApricotBlock:        big.NewInt(0), // TODO specify correct block height
+		ChainID:               AvalancheFujiChainID,
+		HomesteadBlock:        big.NewInt(0),
+		DAOForkBlock:          big.NewInt(0),
+		DAOForkSupport:        true,
+		EIP150Block:           big.NewInt(0),
+		EIP150Hash:            common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"),
+		EIP155Block:           big.NewInt(0),
+		ByzantiumBlock:        big.NewInt(0),
+		ConstantinopleBlock:   big.NewInt(0),
+		PetersburgBlock:       big.NewInt(0),
+		IstanbulBlock:         big.NewInt(0),
+		MuirGlacierBlock:      big.NewInt(0),
+		ApricotBlockTimestamp: new(big.Int).SetUint64(uint64(time.Date(2020, 12, 23, 5, 00, 0, 0, time.UTC).Unix())),
 	}
 
-	// TODO update local network genesis to enable Apricot
-
 	// MainnetChainConfig is the chain parameters to run a node on the main network.
 	MainnetChainConfig = &ChainConfig{
 		ChainID:             big.NewInt(1),
@@ -366,7 +365,11 @@ type ChainConfig struct {
 	YoloV1Block *big.Int `json:"yoloV1Block,omitempty"` // YOLO v1: https://github.com/ethereum/EIPs/pull/2657 (Ephemeral testnet)
 	EWASMBlock  *big.Int `json:"ewasmBlock,omitempty"`  // EWASM switch block (nil = no fork, 0 = already activated)
 
-	ApricotBlock *big.Int `json:"apricotBlock,omitempty"` // Apricot switch block (nil = no fork, 0 = already activated)
+	// Avalanche Network Upgrade Block Timestamps:
+
+	// Apricot switch block timestamp (nil = no fork, 0 = already activated)
+	// Apricot rules go into effect for blocks with a timestamp >= ApricotBlockTimestamp
+	ApricotBlockTimestamp *big.Int `json:"apricotBlock,omitempty"`
 
 	// Various consensus engines
 	Ethash *EthashConfig `json:"ethash,omitempty"`
@@ -403,7 +406,7 @@ func (c *ChainConfig) String() string {
 	default:
 		engine = "unknown"
 	}
-	return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, YOLO v1: %v, Apricot: %v, Engine: %v}",
+	return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, YOLO v1: %v, ApricotTimestamp: %v, Engine: %v}",
 		c.ChainID,
 		c.HomesteadBlock,
 		c.DAOForkBlock,
@@ -417,7 +420,7 @@ func (c *ChainConfig) String() string {
 		c.IstanbulBlock,
 		c.MuirGlacierBlock,
 		c.YoloV1Block,
-		c.ApricotBlock,
+		c.ApricotBlockTimestamp,
 		engine,
 	)
 }
@@ -484,9 +487,12 @@ func (c *ChainConfig) IsEWASM(num *big.Int) bool {
 	return isForked(c.EWASMBlock, num)
 }
 
-// IsApricot returns whether num represents a block number after the Apricot fork
-func (c *ChainConfig) IsApricot(num *big.Int) bool {
-	return isForked(c.ApricotBlock, num)
+// Avalanche Forks:
+
+// IsApricot returns whether num represents a block with a timestamp
+// after the Apricot fork time.
+func (c *ChainConfig) IsApricot(blockTimestamp *big.Int) bool {
+	return isForked(c.ApricotBlockTimestamp, blockTimestamp)
 }
 
 // TODO review how this works and see if it will work for a live transition
@@ -687,6 +693,14 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
 		IsPetersburg:     c.IsPetersburg(num),
 		IsIstanbul:       c.IsIstanbul(num),
 		IsYoloV1:         c.IsYoloV1(num),
-		IsApricot:        c.IsApricot(num),
 	}
 }
+
+// AvalancheRules returns the Avalanche modified rules to support Avalanche
+// network upgrades
+func (c *ChainConfig) AvalancheRules(blockNum, blockTimestamp *big.Int) Rules {
+	rules := c.Rules(blockNum)
+
+	rules.IsApricot = c.IsApricot(blockTimestamp)
+	return rules
+}
diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go
index fc571b9..2732a79 100644
--- a/plugin/evm/vm.go
+++ b/plugin/evm/vm.go
@@ -243,11 +243,10 @@ func (vm *VM) Initialize(
 	// TODO switch to correct block number of the fork
 	switch {
 	case g.Config.ChainID.Cmp(params.AvalancheMainnetChainID) == 0:
-		g.Config.ApricotBlock = nil
+		g.Config = params.AvalancheApricotMainnetChainConfig
 	case g.Config.ChainID.Cmp(params.AvalancheFujiChainID) == 0:
-		g.Config.ApricotBlock = nil
+		g.Config = params.AvalancheApricotFujiChainConfig
 	}
-
 	vm.acceptedDB = prefixdb.New([]byte(acceptedPrefix), db)
 
 	vm.chainID = g.Config.ChainID
-- 
cgit v1.2.3-70-g09d2


From faf845717a170e1c4baebaafef051d32058e2f99 Mon Sep 17 00:00:00 2001
From: Aaron Buchwald <aaron.buchwald56@gmail.com>
Date: Tue, 15 Dec 2020 17:34:48 -0500
Subject: Move params for network upgrade

---
 params/config.go | 11 +++++++++--
 plugin/evm/vm.go |  8 ++++----
 2 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/params/config.go b/params/config.go
index d5db94f..a0b4a51 100644
--- a/params/config.go
+++ b/params/config.go
@@ -53,6 +53,7 @@ var CheckpointOracles = map[common.Hash]*CheckpointOracleConfig{
 	GoerliGenesisHash:  GoerliCheckpointOracle,
 }
 
+// Avalanche ChainIDs
 var (
 	// AvalancheMainnetChainID ...
 	AvalancheMainnetChainID = big.NewInt(43114)
@@ -60,6 +61,12 @@ var (
 	AvalancheFujiChainID = big.NewInt(43113)
 )
 
+// Network upgrade block timestamps
+var (
+	AvalancheMainnetApricotTimestamp = new(big.Int).SetUint64(uint64(time.Date(2021, 1, 7, 5, 00, 0, 0, time.UTC).Unix()))
+	AvalancheFujiApricotTimestamp    = new(big.Int).SetUint64(uint64(time.Date(2020, 12, 23, 5, 00, 0, 0, time.UTC).Unix()))
+)
+
 var (
 	// AvalancheApricotMainnetChainConfig is the configuration for Avalanche Main Network
 	AvalancheApricotMainnetChainConfig = &ChainConfig{
@@ -75,7 +82,7 @@ var (
 		PetersburgBlock:       big.NewInt(0),
 		IstanbulBlock:         big.NewInt(0),
 		MuirGlacierBlock:      big.NewInt(0),
-		ApricotBlockTimestamp: new(big.Int).SetUint64(uint64(time.Date(2021, 1, 7, 5, 00, 0, 0, time.UTC).Unix())),
+		ApricotBlockTimestamp: AvalancheMainnetApricotTimestamp,
 	}
 
 	// AvalancheApricotFujiChainConfig is the configuration for the Fuji Test Network
@@ -92,7 +99,7 @@ var (
 		PetersburgBlock:       big.NewInt(0),
 		IstanbulBlock:         big.NewInt(0),
 		MuirGlacierBlock:      big.NewInt(0),
-		ApricotBlockTimestamp: new(big.Int).SetUint64(uint64(time.Date(2020, 12, 23, 5, 00, 0, 0, time.UTC).Unix())),
+		ApricotBlockTimestamp: AvalancheFujiApricotTimestamp,
 	}
 
 	// MainnetChainConfig is the chain parameters to run a node on the main network.
diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go
index 2732a79..9f5607a 100644
--- a/plugin/evm/vm.go
+++ b/plugin/evm/vm.go
@@ -239,13 +239,13 @@ func (vm *VM) Initialize(
 		return err
 	}
 
-	// Set the Apricot Fork Block on the ChainConfig
-	// TODO switch to correct block number of the fork
+	// If Mainnet or Fuji ChainID is present then switch
+	// manually set the config
 	switch {
 	case g.Config.ChainID.Cmp(params.AvalancheMainnetChainID) == 0:
-		g.Config = params.AvalancheApricotMainnetChainConfig
+		g.Config.ApricotBlockTimestamp = params.AvalancheMainnetApricotTimestamp
 	case g.Config.ChainID.Cmp(params.AvalancheFujiChainID) == 0:
-		g.Config = params.AvalancheApricotFujiChainConfig
+		g.Config.ApricotBlockTimestamp = params.AvalancheFujiApricotTimestamp
 	}
 	vm.acceptedDB = prefixdb.New([]byte(acceptedPrefix), db)
 
-- 
cgit v1.2.3-70-g09d2


From 898c6841f72ba2343682aebe34004d90a4cf3466 Mon Sep 17 00:00:00 2001
From: Aaron Buchwald <aaron.buchwald56@gmail.com>
Date: Tue, 15 Dec 2020 21:22:42 -0500
Subject: modify arc20 example script

---
 examples/arc20/main.go | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/examples/arc20/main.go b/examples/arc20/main.go
index 25f66bf..303d620 100644
--- a/examples/arc20/main.go
+++ b/examples/arc20/main.go
@@ -42,13 +42,9 @@ func init() {
 	address = crypto.PubkeyToAddress(privateKey.PublicKey)
 	// erc20 = common.HexToAddress("0xea75d59faF258F1fdf2b94F158e54D7ad44359B6")
 	// Address of ARC-20 to deposit funds in
-	erc20 = common.HexToAddress("0xC67353d203d748FC6D5E50bf44c7813C39EADea3")
+	erc20 = common.HexToAddress("0x721F9Af7631605713133ccc502E32eA4d43CDfec")
 	// AssetID as a uint256 integer as displayed in Remix of the ARC-20
-	aID, success := new(big.Int).SetString("49381164258359268658146204715775898207620442222804199785202784815303309497754", 10)
-	if !success {
-		panic("Failed to read assetID")
-	}
-	assetID = aID
+	assetID = common.HexToHash("0x42f0d24c970eb6c567ea63a68b97d8e357a7fc8d9f73489c7761f1382b295db4").Big()
 	amount = new(big.Int).SetUint64(100)
 }
 
@@ -74,6 +70,7 @@ func createDepositTransaction(nonce uint64, erc20 common.Address, assetID, amoun
 	return types.NewTransaction(nonce, nativeAssetCallAddr, new(big.Int), gasLimit, gasPrice, callData)
 }
 
+// deploy ARC-20 contract with specific assetID
 func main() {
 	client, err := ethclient.Dial(uri)
 	if err != nil {
-- 
cgit v1.2.3-70-g09d2


From 8026c5f6c44a02ff44bb25cb941915f53cdc9e1c Mon Sep 17 00:00:00 2001
From: Aaron Buchwald <aaron.buchwald56@gmail.com>
Date: Tue, 15 Dec 2020 21:24:34 -0500
Subject: Change name of apricot block in config

---
 params/config.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/params/config.go b/params/config.go
index a0b4a51..2f59170 100644
--- a/params/config.go
+++ b/params/config.go
@@ -376,7 +376,7 @@ type ChainConfig struct {
 
 	// Apricot switch block timestamp (nil = no fork, 0 = already activated)
 	// Apricot rules go into effect for blocks with a timestamp >= ApricotBlockTimestamp
-	ApricotBlockTimestamp *big.Int `json:"apricotBlock,omitempty"`
+	ApricotBlockTimestamp *big.Int `json:"apricotBlockTimestamp,omitempty"`
 
 	// Various consensus engines
 	Ethash *EthashConfig `json:"ethash,omitempty"`
-- 
cgit v1.2.3-70-g09d2


From 7c758da302baf80775876008958ec77055ab953a Mon Sep 17 00:00:00 2001
From: Aaron Buchwald <aaron.buchwald56@gmail.com>
Date: Tue, 15 Dec 2020 21:30:16 -0500
Subject: Remove contracts

---
 contracts/arc20.sol        | 150 ---------------------------------------------
 contracts/nativeAssets.sol |  20 ------
 2 files changed, 170 deletions(-)
 delete mode 100644 contracts/arc20.sol
 delete mode 100644 contracts/nativeAssets.sol

diff --git a/contracts/arc20.sol b/contracts/arc20.sol
deleted file mode 100644
index d1af6a3..0000000
--- a/contracts/arc20.sol
+++ /dev/null
@@ -1,150 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-pragma solidity >=0.6.0 <0.8.0;
-
-import {NativeAssets} from "./nativeAssets.sol";
-
-contract ARC20 {
-
-    mapping (address => uint256) private _balances;
-    mapping(address => mapping(address => uint256)) private _allowances;
-
-    uint256 private _assetID;
-
-    uint256 private _totalSupply;
-
-    string private _name;
-    string private _symbol;
-    uint8 private _decimals;
-
-    constructor(string memory name_, string memory symbol_, uint8 decimals_, uint256 assetID_) public {
-        _name = name_;
-        _symbol = symbol_;
-        _decimals = decimals_;
-        _assetID = assetID_;
-    }
-
-    /**
-     * @dev Returns the name of the token.
-     */
-    function name() public view returns (string memory) {
-        return _name;
-    }
-
-    /**
-     * @dev Returns the symbol of the token, usually a shorter version of the
-     * name.
-     */
-    function symbol() public view returns (string memory) {
-        return _symbol;
-    }
-
-    /**
-     * @dev Returns the number of decimals used to represent the token.
-     */
-    function decimals() public view returns (uint8) {
-        return _decimals;
-    }
-
-    /**
-     * @dev Returns the total supply of `assetID` currently held by
-     * this contract.
-     */
-    function totalSupply() public view returns (uint256) {
-        return _totalSupply;
-    }
-
-    /**
-     * @dev Returns the balance of `account` held in this contract.
-     */
-    function balanceOf(address account) public view returns (uint256) {
-        return _balances[account];
-    }
-
-    // Withdrawal/Deposit functionality
-
-    /**
-     * @dev Acknowledges the receipt of some amount of an Avalanche Native Token
-     * into the contract implementing this interface.
-     */
-    function deposit() public {
-        uint256 updatedBalance = NativeAssets.assetBalance(address(this), _assetID);
-        uint256 depositAmount = updatedBalance - _totalSupply;
-        assert(depositAmount >= 0);
-
-        _balances[msg.sender] += depositAmount;
-        _totalSupply = updatedBalance;
-        emit Deposit(msg.sender, depositAmount);
-    }
-
-    /**
-     * @dev Emitted when `value` tokens are deposited from `depositor`
-     */
-    event Deposit(address indexed depositor, uint256 value);
-
-    /**
-     * @dev Withdraws `value` of the underlying asset to the contract
-     * caller.
-     */
-    function withdraw(uint256 value) public {
-        require(_balances[msg.sender] >= value, "Insufficient funds for withdrawal");
-        
-        _balances[msg.sender] -= value;
-        _totalSupply -= value;
-
-        NativeAssets.assetCall(msg.sender, _assetID, value, "");
-        emit Withdrawal(msg.sender, value);
-    }
-
-    /**
-     * @dev Emitted when `value` tokens are withdrawn to `withdrawer`
-     */
-    event Withdrawal(address indexed withdrawer, uint256 value);
-
-    /**
-     * @dev Returns the `assetID` of the underlying asset this contract handles.
-     */
-    function assetID() external view returns (uint256) {
-        return _assetID;
-    }
-
-    event Transfer(address indexed from, address indexed to, uint256 value);
-
-    function transfer(address to, uint256 value) public returns (bool success) {
-        require(_balances[msg.sender] >= value, "insufficient balance for transfer");
-
-        _balances[msg.sender] -= value;  // deduct from sender's balance
-        _balances[to] += value;          // add to recipient's balance
-        emit Transfer(msg.sender, to, value);
-        return true;
-    }
-
-    event Approval(address indexed owner, address indexed spender, uint256 value);
-
-    function approve(address spender, uint256 value)
-        public
-        returns (bool success)
-    {
-        _allowances[msg.sender][spender] = value;
-        emit Approval(msg.sender, spender, value);
-        return true;
-    }
-
-    function transferFrom(address from, address to, uint256 value)
-        public
-        returns (bool success)
-    {
-        require(value <= _balances[from], "From address has insufficient balance to transfer");
-        require(value <= _allowances[from][msg.sender], "Insufficient allowance granted to sender");
-
-        _balances[from] -= value;
-        _balances[to] += value;
-        _allowances[from][msg.sender] -= value;
-        emit Transfer(from, to, value);
-        return true;
-    }
-
-    function allowance(address owner, address spender) public view returns (uint256) {
-        return _allowances[owner][spender];
-    }
-}
diff --git a/contracts/nativeAssets.sol b/contracts/nativeAssets.sol
deleted file mode 100644
index 836c4d8..0000000
--- a/contracts/nativeAssets.sol
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-pragma solidity >=0.6.0 <0.8.0;
-
-library NativeAssets {
-    address constant balanceAddr = 0x0100000000000000000000000000000000000001;
-    address constant transferAddr = 0x0100000000000000000000000000000000000002;
-
-    function assetBalance(address addr, uint256 assetID) public returns (uint256) {
-        (bool success, bytes memory data) = balanceAddr.call(abi.encodePacked(addr, assetID));
-        require(success, "assetBalance failed");
-        return abi.decode(data, (uint256));
-    }
-    
-    function assetCall(address addr, uint256 assetID, uint256 assetAmount, bytes memory callData) public returns (bytes memory) {
-        (bool success, bytes memory data) = transferAddr.call(abi.encodePacked(addr, assetID, assetAmount, callData));
-        require(success, "assetCall failed");
-        return data;
-    }
-}
-- 
cgit v1.2.3-70-g09d2