tableturn / tt-white-contracts

The Consilience Group Whitelabeling Smart Contracts.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

TT White Contracts

This repository contains the Ethereum Smart Contracts that are used for our Tokenization as a Service (TSaaS) platform. This project uses Hardhat to allow for streamlined development and testing, as well as some helpful tasks (see ./src/tasks).

This means that regardless of the network you're using (local, staging, production etc), the address of the deployed contracts should remain the same.

Contract Folder Structure

Here is an overview of the layout of the contracts folder:

  • lib/ and interfaces/ are commonly used across the project, and aren't specific to any domain.
  • The issuer/, marketplace/ and fast/ folders are diamond definitions. Each contains:
    • Top-level facets for each diamond.
    • A lib/ folder containing abstractions and storage libraries for each facet.

Bootstrapping a Functional System Locally

For development systems, we use local signers (Eg ethers.getSigners()). In the following paragraphs, you can assume that:

  • zero_address is 0x0000000000000000000000000000000000000000.
  • deployer is the very first signer from the signers list.
  • issuerMember is the second signer from the signers list.
  • governor is the third signer from the signers list.
  • member is the fourth signer from the signers list.
  • random is a random - non-signer at address 0xF7e5800E52318834E8689c37dCCCD2230427a905.

Before starting a node, it is recommended to clean your local deployment folder (rm -rf deployments/localhost). Then, you can run yarn hardhat node. You'll notice that both the Issuer and Marketplace contracts are being deployed automatically.

You then probably might want to jump directly to the fast-deploy task of this document to get started.

Account Tasks (See src/tasks/accounts.ts)

To provision an account with ETH, you can run:

yarn hardhat faucet 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 \
              --network localhost

Or if you're in a hurry and you would like all available signers to obtain an outrageous amount of ETH, you can run:

yarn hardhat make-us-rich \
              --network localhost

Top-Level Tasks (See src/tasks/issuer.ts)

Although the local node you're running should already have an Issuer and an Marketplace diamond automatically deployed, you could deploy one yourself if running on a completely clean chain:

yarn hardhat issuer-deploy \
              --network localhost \
              --member 0x70997970c51812dc3a010c7d01b50e0d17dc79c8

Note that you won't need to run this particular task if you're using a local development node, as the migration scripts in deploy/ are ran automatically upon starting it.

FAST Token Tasks (See src/tasks/fast.ts)

Then you can start deploying FAST:

yarn hardhat fast-deploy \
              --network localhost \
              --governor 0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc \
              --name "Some Awesome FAST Stuff" \
              --symbol "SAF" \
              --decimals 18 \
              --has-fixed-supply false \
              --is-semi-public true \
              --crowdfunds-default-basis-points-fee 250 \
              --mint 1000000

This task automatically deploys a full FAST diamond including its initialization facet. It then calls the FastInitFacet.initialize/0 function, and lastly performs a diamond cut to remove the initialization facet.

Once at least one FAST is deployed, take note of its symbol. There are more tasks that you can run over a particular FAST.

For example, to mint new tokens:

yarn hardhat fast-mint SAF \
              --network localhost \
              --amount 1000000 \
              --ref "Much tokens, very wow, such bling."

To obtain the balance of an account over a particular FAST:

yarn hardhat fast-balance SAF \
              --network localhost \
              --account 0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc

If you would like to query the minted (unallocated) tokens, you can instead query address zero:

yarn hardhat fast-balance SAF \
              --network localhost \
              --account 0x0000000000000000000000000000000000000000

Distributions

Here is an end-to-end scenario showcasing how to use distributions.

// We'll use the `user1` named account to be the owner of the distribution.
let { issuerMember, automaton, user1, user2, user3, user4, user5 } = await getNamedAccounts();
let issuerSigner = await ethers.getSigner(issuerMember);
let automatonSigner = await ethers.getSigner(automaton);
let userSigner = await ethers.getSigner(user1);
// Get our dummy ERC20 token, and bind it to our user as the caller.
let token = (await ethers.getContract('ERC20')).connect(userSigner);
// Mint 5000 tokens for that user.
await token.mint(userSigner.address, 5000);
// Get a handle to the Issuer contract and `F01` FAST, and bind it to our user as the caller.
let issuer = await ethers.getContract('Issuer');
let fast = await ethers.getContract('FastF01');
// Add the automaton as a distribution manager.
await fast.connect(issuerSigner).setAutomatonPrivileges(automaton, 2 /* FAST_PRIVILEGE_MANAGE_DISTRIBUTIONS */);

// Let our user approve 100 tokens to be spent by our FAST contract.
await token.connect(userSigner).approve(fast.address, 110);
// At this point, the allowance from our user to the FAST contract should be 110;
(await token.allowance(user1, fast.address)).toString();
// Have the user create a new distribution. It will deploy a new Distribution contract in the Fund phase.
await fast.connect(userSigner).createDistribution(token.address, 110, 0);

// Get the address and handle of the newly deployed contract.
let [[distAddr]] = await fast.paginateDistributions(0, 1);
let dist = await ethers.getContractAt('Distribution', distAddr);
// At this point, the fee needs to be set.
await dist.connect(automatonSigner).advanceToBeneficiariesSetup(10);
// Set them up as beneficiaries of the distribution.
await dist.connect(automatonSigner).addBeneficiaries([user2, user3, user4], [10, 20, 30]);
// Advance to the Withdrawal phase - it should fail, since the fee plus all amounts doesn't take all the available funds.
await dist.connect(automatonSigner).advanceToWithdrawal();
// Add a beneficiary to make sure all available funds are distributed.
await dist.connect(issuerSigner).addBeneficiaries([user5], [40]);
// Advance to the Withdrawal phase - this time it should succeed.
await dist.connect(automatonSigner).advanceToWithdrawal();
// At this point, the issuer should already have received their fee - it should be 10.
(await token.balanceOf(issuer.address)).toString();

// Our beneficiaries should be able to withdraw from the Distribution.
await Promise.all([user2, user3, user4, user5].map((u) => dist.withdraw(u)));
// Check the token balance of the beneficiaries - it should be [10, 20, 30, 40].
await Promise.all([issuer.address, user2, user3, user4, user5].map((u) => token.balanceOf(u))).then(
  (b) => b.map((b) => b.toString())
);

// Now let the issuer member wire all the ERC20 to themselves.
(await
  issuer
    .connect(issuerSigner)
    .transferERC20Tokens(token.address, await token.balanceOf(issuer.address), issuerMember)
);
// The issuer contract should now have no ERC20 tokens, while the issuer member should have them all.
(await Promise.all([issuer.address, issuerMember].map(u => token.balanceOf(u)))).map(b => b.toString());

Crowdfunds

Here are an end-to-end scenarios showcasing how to use crowdfunds.

Successful Crowdfund

// We'll use the `user1` named account to be the owner of the distribution.
let { issuerMember, user1, user2, user3, user4 } = await getNamedAccounts();
let issuerSigner = await ethers.getSigner(issuerMember);
let userSigner = await ethers.getSigner(user1);
let [user1Signer, user2Signer, user3Signer] = await Promise.all([user1, user2, user3].map(user => ethers.getSigner(user)));

// Get our dummy ERC20 token, and bind it to our user as the caller.
let token = (await ethers.getContract('ERC20')).connect(userSigner);
// Mint 5000 tokens for our users.
await Promise.all([user1, user2, user3].map(user => token.mint(user, 5000)));
// Get a handle to the Issuer contract and `F01` FAST, and bind it to our user as the caller.
let issuer = await ethers.getContract('Issuer');
let fast = await ethers.getContract('FastF01');

// We're ready to start a crowdfund.
await fast.connect(user1Signer).createCrowdfund(token.address, user4);
let [[crowdfundAddr]] = await fast.paginateCrowdfunds(0, 1);
let crowdfund = await ethers.getContractAt('Crowdfund', crowdfundAddr);
// Have the issuer set a 20% fee (expressed in basis point - 2_000).
await crowdfund.connect(issuerSigner).advanceToFunding(2_000);

// Have each user pledge.
await Promise.all([user1Signer, user2Signer, user3Signer].map((user) => {
  token.connect(user).approve(crowdfundAddr, 500).then(() => crowdfund.connect(user).pledge(500))
}));
// At this point, the total pledge should be 1500.
(await crowdfund.collected()).toString();
// The fee should be 20% of 1500: 300.
(await crowdfund.feeAmount()).toString();

// Have the issuer declare the crowdfund a success.
await crowdfund.connect(issuerSigner).terminate(true);
// The crowdfund should now be terminated with Success.
await crowdfund.phase();
// The issuer contract should have received the fee.
(await token.balanceOf(issuer.address)).toString();
// The beneficiary should have received the rest.
(await token.balanceOf(user4)).toString();

// Users should not be able to get their refund.
await crowdfund.refund(user1);

Failed Crowdfund

// We'll use the `user1` named account to be the owner of the distribution.
let { issuerMember, user1, user2, user3, user4 } = await getNamedAccounts();
let issuerSigner = await ethers.getSigner(issuerMember);
let userSigner = await ethers.getSigner(user1);
let [user1Signer, user2Signer, user3Signer] = await Promise.all([user1, user2, user3].map(user => ethers.getSigner(user)));

// Get our dummy ERC20 token, and bind it to our user as the caller.
let token = (await ethers.getContract('ERC20')).connect(userSigner);
// Mint 5000 tokens for our users.
await Promise.all([user1, user2, user3].map(user => token.mint(user, 5000)));
// Get a handle to the Issuer contract and `F01` FAST, and bind it to our user as the caller.
let issuer = await ethers.getContract('Issuer');
let fast = await ethers.getContract('FastF01');

// We're ready to start a crowdfund.
await fast.connect(user1Signer).createCrowdfund(token.address, user4);
let [[crowdfundAddr]] = await fast.paginateCrowdfunds(0, 1);
let crowdfund = await ethers.getContractAt('Crowdfund', crowdfundAddr);
// Have the issuer set a 20% fee (expressed in basis point - 2_000).
await crowdfund.connect(issuerSigner).advanceToFunding(2_000);

// Have each user pledge.
await Promise.all([user1Signer, user2Signer, user3Signer].map((user) => {
  token.connect(user).approve(crowdfundAddr, 500).then(() => crowdfund.connect(user).pledge(500))
}));
// At this point, the total pledge should be 1500.
(await crowdfund.collected()).toString();
// The fee should be 20% of 1500: 300.
(await crowdfund.feeAmount()).toString();

// Have the issuer declare the crowdfund a success.
await crowdfund.connect(issuerSigner).terminate(false);
// The crowdfund should now be terminated with Failure.
await crowdfund.phase();
// The issuer contract should **not** have received the fee.
(await token.balanceOf(issuer.address)).toString();
// The beneficiary should **not** have received the rest.
(await token.balanceOf(user4)).toString();

// Users should not be able to get their refund.
await Promise.all([user1, user2, user3].map(user => crowdfund.refund(user)));
// Balances should have been reverted for all pledgers.
(await Promise.all([user1, user2, user3].map(user => token.balanceOf(user)))).map(balance => balance.toString());

Hardhat Cheat-Sheet

Here are a few useful commands:

# Displays some help.
yarn hardhat help
# Compiles the contracts and generates artifacts.
yarn hardhat compile
# Cleans the build environment.
yarn hardhat clean
# Runs the test suite.
yarn hardhat test
# Runs the test suite, and reports gas usage.
REPORT_GAS=true yarn hardhat test
# Starts a local blockchain node.
yarn hardhat node
# Reports coverage.
yarn hardhat coverage

About

The Consilience Group Whitelabeling Smart Contracts.


Languages

Language:TypeScript 61.9%Language:Solidity 37.7%Language:Shell 0.2%Language:JavaScript 0.1%Language:Dockerfile 0.1%