⚠ Note: as of mid 2023, this codebase is not being maintained. It might work, it might not. If you find a bug, feel free to report it, but do not expect it to be fixed. If you do a PR where tests pass, we're happy to merge it. And feel free to fork this repo and change it as you wish (including bug fixes).
TokenSPICE simulates tokenized ecosystems via an agent-based approach, with EVM in-the-loop.
It can help in Token Engineering flows, to design, tune, and verify tokenized ecosystems. It's young but promising. We welcome you to contribute! 👋
- TokenSPICE simulates by simply running a loop. At each iteration, each agent in the netlist takes a step. That's it! Simple is good.
- A netlist wires up a collection of agents to interact in a given way. Each agent is a class. It has an Ethereum wallet, and does work to earn money. Agents may be written in pure Python, or with an EVM-based backend.
- One models a system by writing a netlist and tracking metrics (KPIs). One can write their own netlists and agents to simulate whatever they like. The netlists directory has examples.
- 🏗 Initial Setup
- 🏄 Running, Debugging
- 🦑 Agents and Netlists
- 🐟 Updating Envt
- 🐡 Backlog
- 🐋 Benefits of EVM Agent Simulation
- 🦈 Resources
- 🏛 License
- Linux/MacOS
- Python 3.8.5+
- solc 0.8.0+ [Instructions]
- ganache. To install:
npm install ganache --global
- nvm 16.13.2, not nvm 17. To install:
nvm install 16.13.2; nvm use 16.13.2
. [Details]
Open a new terminal and:
#clone repo
git clone https://github.com/tokenspice/tokenspice
cd tokenspice
#create a virtual environment
python3 -m venv venv
#activate env
source venv/bin/activate
#install dependencies
pip install -r requirements.txt
#install brownie packages (you can ignore FileExistsErrors)
./brownie-install.sh
Potential issues & workarounds
- Issue: Brownie doesn't support Python 3.11 yet. Workaround: before "install dependencies" step above, run
pip install vyper==0.3.7 --ignore-requires-python
andsudo apt-get install python3.11-dev
- Issue: MacOS might flag "Unsupported architecture". Workaround: install including ARCHFLAGS:
ARCHFLAGS="-arch x86_64" pip install -r requirements.txt
From "Prerequisites", you should have Ganache installed.
Open a new console and go to tokenspice directory. Then:
source venv/bin/activate
./ganache.py
This will start a Ganache chain, and populate 9 accounts.
tsp
is the command-line interface for TokenSPICE.
Open a new console and go to tokenspice directory. Then:
source venv/bin/activate
#add pwd to bash path
export PATH=$PATH:.
#see tsp help
tsp
NOTE: if you have a directory named contracts
from before, which is side-by-side with your tokenspice
directory, you'll get issues. To avoid this, rename or move that contracts directory.
From the same terminal:
#install 3rd party libs, then call "brownie compile" in sol057/ and sol080/
tsp compile
TokenSPICE sees smart contracts as classes. How:
- When it starts, it calls
brownie.project.load('./sol057', name="MyProject")
to load the ABIs in./sol057/build/
. Similar forsol080
. - That's enough info to treat each contract in
sol057/contracts/
as a class. Then, calldeploy()
on it to create a new object.
From terminal:
#run single test. It uses brownie, which auto-starts Ganache local blockchain node.
pytest sol057/contracts/simpletoken/test/test_Simpletoken.py::test_transfer
#run all of a directory's tests
pytest sol057/contracts/simpletoken/test
#run all unit tests
pytest
#run static type-checking. By default, uses config mypy.ini. Note: pytest does dynamic type-checking.
mypy ./
#run linting on code style
pylint *
#auto-fix some pylint complaints
black ./
Go here for details on linting / style.
From terminal:
#run simulation, sending results to 'outdir_csv' (clear dir first, to be sure)
rm -rf outdir_csv; tsp run netlists/scheduler/netlist.py outdir_csv
You'll see an output like:
Arguments: NETLIST=netlists/...
Launching 'ganache-cli --accounts 10 --hardfork ...
mnemonic: 'sausage bunker giant drum ...
INFO:master:Begin.
INFO:master:SimStrategy={OCEAN_funded=5.0, duration_seconds=157680000, ...}
INFO:master:Tick=0 (0.0 h, 0.0 d, 0.0 mo, 0.0 y); timestamp=1642844072; OCEAN_vested=0, ...
INFO:master:Tick=3 (2160.0 h, 90.0 d, 3.0 mo, 0.2 y); timestamp=1650620073; OCEAN_vested=0.0, ...
INFO:master:Tick=6 (4320.0 h, 180.0 d, 6.0 mo, 0.5 y); timestamp=1658396073; OCEAN_vested=0.0, ...
INFO:master:Tick=9 (6480.0 h, 270.0 d, 9.0 mo, 0.7 y); timestamp=1666172074; OCEAN_vested=0.0, ...
INFO:master:Tick=12 (8640.0 h, 360.0 d, 12.0 mo, 1.0 y); timestamp=1673948074; OCEAN_vested=0.0, ...
INFO:master:Tick=15 (10800.0 h, 450.0 d, 15.0 mo, 1.2 y); timestamp=1681724074; OCEAN_vested=0.232876 ...
Now, let's view the results visually. In the same terminal:
#create output plots in 'outdir_png' (clear dir first, to be sure)
rm -rf outdir_png; tsp plot netlists/scheduler/netlist.py outdir_csv outdir_png
#view plots
eog outdir_png
To see the blockchain txs apart from the other logs: open a new terminal and:
#activate env't
cd tokenspice
source venv/bin/activate
#run ganache
export PATH=$PATH:.
tsp ganache
Now, from your original terminal:
#run the sim. It will auto-connect to ganache
rm -rf outdir_csv; tsp run netlists/scheduler/netlist.py outdir_csv
For longer runs (eg wsloop), we can log to a file while watching the console in real-time:
#run the sim in the background, logging to out.txt
rm -rf outdir_csv; tsp run netlists/wsloop/netlist.py outdir_csv > out.txt 2>&1 &
#monitor in real-time
tail -f out.txt
To kill a sim in the background:
#find the background process
ps ax |grep "tsp run"
#example result:
#223429 pts/4 Rl 0:02 python ./tsp run netlists/wsloop/netlist.py outdir_csv
#to kill it:
kill 223429
Brownie console is a Python console, with some extra Brownie goodness, so that we can interactively play with Solidity contracts as Python classes, and deployed Solidity contracts as Python objects.
From terminal:
#brownie needs a directory with ./contracts/. Go to one.
cd sol057/
#start console
brownie console
In brownie console:
>>> st = Simpletoken.deploy("DT1", "Simpletoken 1", 18, Wei('100 ether'), {'from': accounts[0], "priority_fee": chain.priority_fee, "max_fee": chain.base_fee + 2 *
chain.priority_fee})
Transaction sent: 0x9d20d3239d5c8b8a029f037fe573c343efd9361efd4d99307e0f5be7499367ab
Gas price: 0.0 gwei Gas limit: 6721975
Simpletoken.constructor confirmed - Block: 1 Gas used: 601010 (8.94%)
Simpletoken deployed at: 0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87
>>> st.symbol()
'DT1'
>>> st.balanceOf(accounts[0])/1e18
>>> dir(st)
[abi, address, allowance, approve, balance, balanceOf, bytecode, decimals, decode_input, get_method, get_method_object, info, name, selectors, signatures, symbol, topics, totalSupply, transfer, transferFrom, tx]
Agents are defined at agents/
. Agents are in a separate directory than netlists, to facilitate reuse across many netlists.
All agents are written in Python. Some may include EVM behavior (more on this later).
Each Agent has an AgentWallet
, which holds a Web3Wallet
. The Web3Wallet
holds a private key and creates transactions (txs).
The netlist defines what you simulate, and how.
Netlists are defined at netlists/
. You can reuse existing netlists or create your own.
TokenSPICE expects a netlist module (in a netlist.py file) that defines these specific classes and functions:
SimStrategy
class: simulation run parametersKPIs
class andnetlist_createLogData()
function: what metrics to log during the runnetlist_plotInstructions()
function: how to plot the metrics after the runSimState
class: system-level structure & parameters, i.e. how agents are instantiated and connected. It imports agents defined inagents/*Agent.py
. Some agents use EVM. You can add and edit Agents to suit your needs.
There are two practical ways to specify SimStrategy
, KPIs
, and so on for netlist.py:
-
For simple netlists. Have just one file (
netlist.py
) to hold all the code for each class and method given above. This is appropriate for simple netlists, like simplegrant (just Python) and simplepool (Python+EVM). -
For complex netlists. Have one or more separate files for each class and method given above, such as
netlists/NETLISTX/SimStrategy.py
. Then, import them all intonetlist.py
file to unify their scope to a single module (netlist
). This allows for arbitrary levels of netlist complexity. The wsloop netlist is a good example. It models the Web3 Sustainability Loop, which is inspired by the Amazon flywheel and used by Ocean, Boson and others as their system-level token design.
The class SimState
defines which agents are used. Some agents even spawn other agents. Each agent object is stored in the SimState.agents
object, a dict with some added querying abilities. Key SimState
methods to access this object are addAgent(agent)
, getAgent(name:str)
, allAgents()
, and numAgents()
. SimStateBase
has details.
Every iteration of the engine make a call to each agent's takeStep()
method. The implementation of GrantGivingAgent.takeStep()
is shown below. Lines 26–33 determine whether it should disburse funds on this tick. Lines 35–37 do the disbursal if appropriate.
There are no real constraints on how an agent's takeStep()
is implemented. This which gives great TokenSPICE flexibility in agent-based simulation. For example, it can loop in EVM, like we show later.
Here are some existing netlists.
- simplegrant - granter plus receiver, that's all. No EVM.
- simplepool - publisher that periodically creates new pools. EVM.
- scheduler - scheduled vesting from a wallet. EVM.
- wsloop - Web3 Sustainability Loop. No EVM.
- oceanv3 - Ocean Market V3 - initial design. EVM.
- oceanv4 - Ocean Market V4 - solves rug pulls. EVM.
To learn more about how TokenSPICE netlists are structured, we refer you to the simplegrant (pure Python) and simplepool (Python+EVM) netlists, which each have more thorough explainers.
Larger things we'd like to see:
- Higher-level tools that use TokenSPICE, including design entry, verification, design space exploration, and more.
- Improvements to TokenSPICE itself in the form of faster simulation speed, improved UX, and more.
See this board: https://github.com/orgs/tokenspice/projects/1/views/1
TokenSPICE and other EVM agent simulators have these benefits:
- Faster and less error prone, because the model = the Solidity code. Don’t have to port any existing Solidity code into Python, just wrap it. Don’t have to write lower-fidelity equations.
- Enables rapid iterations of writing Solidity code -> simulating -> changing Solidity code -> simulating.
- Super high fidelity simulations, since it uses the actual code itself. Enables modeling of design, random and worst-case variables.
- Mental model is general enough to extend to Vyper, LLL, and direct EVM bytecode. Can extend to non-EVM blockchain, and multi-chain scenarios.
- Opportunity for real-time analysis / optimization / etc against live chains: grab the latest chain’s snapshot into ganache, run a local analysis / optimization etc for a few seconds or minutes, then do transaction(s) on the live chain. This can lead to trading systems, failure monitoring, more.
Here are further resources.
- TokenSPICE medium posts, starting with "Introducing TokenSPICE"
- Intro to SPICE & TokenSPICE [Gslides - short] [Gslides - long]
- TE for Ocean V3 [GSlides] [video] , TE Community Workshop, Dec 9, 2020
- TE for Ocean V4 [GSlides] [slides] [video] , TE Academy, May 21, 2021
History:
- TokenSPICE was initially built to model the Web3 Sustainability Loop. It's now been generalized to support EVM, on arbitary netlists.
- Most initial work was by trentmc (Ocean Protocol); several more contributors have joined since 👪👨👩👧👧
Art:
- TokenSPICE logos
- Fishnado image sources (CC): [1] [2]
Other links:
- Twitter: @tokenspice
- Medium: @tokenspice
The license is MIT. Details