This project solves the problem of allowing payers of token streams to create streams and fund them after the fact, i.e. first create the stream, then fund it later when funds are available.
For example, Nouns DAO can use Token Buyer with Streamer and create proposals that:
- Create a Stream
- Ask TokenBuyer's Payer contract to send tokens to the newly created Stream when it has the funds
- TokenBuyer acuiqres the funds, sends them to Payer, Payer funds the Stream
- The Stream's recipient can start withdrawing funds
-
- Creates new Streams using minimal clones
- Supports predicting new Stream contract addresses, making it easier to compose DAO proposals, since a proposal author can know the address of the Stream the proposal will create before it's created
-
- Supports custom start and end timestamps
- Does not enforce upfront funding, making it usable for DAOs and other payers that acquire payment tokens post Stream creation
To run smart contract tests, make sure you have Foundry installed, then run:
forge install
forge test
Run Foundry's local node:
anvil
Copy one of anvil's test account's address and private key, to use in commands below.
Deploy StreamFactory:
forge script script/DeployStreamFactory.s.sol --rpc-url http://127.0.0.1:8545 --broadcast --sender <anvil test account address> --private-key <anvil test account private key>
Deploy MockToken, which you can use to mint tokens to your test Streams:
forge script script/DeployMockToken.s.sol --rpc-url http://127.0.0.1:8545 --broadcast --sender <anvil test account address> --private-key <anvil test account private key>
Go to ./broadcast/DeployStreamFactory.s.sol/31337/run-latest.json
and copy the value of contractAddress
for StreamFactory
.
Go to ./broadcast/DeployMockToken.s.sol/31337/run-latest.json
and copy the value of contractAddress
for ERC20Mock
.
Copy another anvil test account and private key to use as your stream recipient.
If you want to create a stream that starts or ends in the future, you can use foundry.toml
to override anvil's first block timestamp, or run cast block latest
and copy the timestamp of the current block as a starting point to your stream.
Simulate stream creation:
cast call --rpc-url http://127.0.0.1:8545 --private-key <your first anvil test account private key> <your StreamFactory address> "createStream(address,uint256,address,uint256,uint256)" <your second test account, the recipient of the stream> <the token amount to stream, e.g. 1000> <your ERC20Mock contract address> <stream start timestamp, e.g. as taken from running cast block latest above> <stream end timestamp, e.g. start time + 1000 to make it predictable>
The output will look something like:
0x00000000000000000000000033f456c89902746ffed70041f8bc8672e3a171fd
This is the expected new stream address, you just need to trim it down a bit, in this case to be: 0x33f456c89902746ffed70041f8bc8672e3a171fd
.
Execute stream creation using the same inputs, by running the same command as above, only using cast send
instead of cast call
.
To make sure you're stream has been created, get the recipient's balance; it should be greater than zero:
cast call --rpc-url http://127.0.0.1:8545 --private-key <a test account private key> <stream contract address> "balanceOf(address)(uint256)" <stream recipient address>
To withdraw, you'll need to mint tokens to fund the stream, then call withdraw using the recipient account:
cast send --rpc-url http://127.0.0.1:8545 --private-key <a test account private key> <the ERC20Mock contract address> "mint(address,uint256)" <the stream contract address> <the stream amount, e.g. 1000>
cast send --rpc-url http://127.0.0.1:8545 --private-key <the payer or recipient account private key> <the stream contract address> "withdraw(uint256)" <the withdrawal amount, should not exceed recipient current balance>
In ./subgraph/networks.json
, make sure StreamFactory's address matches the one you recently deployed.
Spin up your local node: (you need Docker installed and running in the background)
yarn local-node
Then run:
yarn
yarn codegen
yarn build:local
yarn create:local
yarn deploy:local
You should now be able to query the subgraph and see your recently created streams and any withdrawals and cancellation events related to it. You can use Postman or any other HTTP request tool, send a POST request to http://localhost:8000/subgraphs/name/streamer
with a GraphQL query like:
query {
streams (limit: 10) {
id
createdAt
createdBy
payer
recipient
tokenAmount
tokenAddress
startTime
stopTime
withdrawals {
id
withdrawnAt
executedBy
amount
}
cancellations {
id
cancelledAt
cancelledBy
payerBalance
recipientBalance
}
}
}
Copy ./env.example
to ./.env
and set your values.
Deploy StreamFactory:
forge script script/DeployStreamFactory.s.sol --verify -vvvvv --rpc-url https://sepolia.infura.io/v3/<infura API key> --broadcast --sender <your deployer account address> -i 1
Deploy ERC20Mock:
forge script script/DeployMockToken.s.sol --verify -vvvvv --rpc-url https://sepolia.infura.io/v3/<infura API key> --broadcast --sender <your deployer account address> -i 1
At the time of writing, the hosted service does not support Sepolia, but we can still run a local node.
In ./subgraph/networks.json
, make sure StreamFactory's address matches the most recent deployment you want to use; you might find it under ./broadcast/DeployStreamFactory.s.sol/11155111/run-latest.json
.
Copy ./subgraph/.env.example
to ./subgraph/.env
and set network name and RPC env vars - these are used in docker-compose.yml
.
Spin up your local node: (you need Docker installed and running in the background)
yarn local-node
Then run:
yarn
yarn codegen
yarn build:sepolia
yarn create:sepolia
yarn deploy:sepolia
Querying works as outlined above for local testing.
Contract | Address |
---|---|
StreamFactory | 0xb2fFEEF1F68CfacDeFdAFe6F1a9D30Ff47C7cB5e |
Stream | 0x780268514e532F296b9Bf47D7c5EE515b24f8872 |
Contract | Address |
---|---|
StreamFactory | 0xc08a287eCB16CeD801f28Bb011924f7DE5Cc53a3 |
Stream | 0x20ffE6b3b08f5009788812583D42C5b42bf44447 |