Git Product home page Git Product logo

openst.js's Introduction

OpenST.js

OpenST is a framework for building token economies. OpenST supports interactions with openst-contracts. It requires openst-contracts for ABIs and BINs of contracts.

Build master Build develop npm version Discuss on Discourse Chat on Gitter

Setup

This library assumes that nodejs and geth are installed and running. To install OpenST.js in your project using npm:

$ npm install @openst/openst.js --save

The code works for Ethereum Byzantium and Petersburg.

Creating an OpenST object

// Creating OpenST.js object
const OpenST = require('@openst/openst.js');

Deploying contracts

Constants

Before deploying contracts, please set some constants to funded addresses that you control.

// Initialize web3 object using the geth endpoint
const Web3 = require('web3');
const web3Provider = new Web3('http://127.0.0.1:8545');

// deployer address
const deployerAddress = '0xaabb1122....................';

// Worker address
const worker = '0xaabb1122....................';

// Relayer address
const relayer = '0xaabb1122....................';

// It's needed for unlock account before doing transaction.
const passphrase = 'some passphrase.....';

// Other constants
const gasPrice = '0x12A05F200';
const gas = '7500000';

Deploy Organization contract

An Organization contract serves as an on-chain access control mechanism by assigning roles to a set of addresses.

const { Organization } = OpenST.ContractInteract;
const organizationOwner = '0xaabb1122....................';
const admin = '0xaabb1122....................';
const orgConfig = {
  deployer: deployerAddress,
  owner: organizationOwner,
  admin: admin,
  workers: [worker],
  workerExpirationHeight: '200000000' // This is the block height for when the the worker is set to expire.
};
const txOptions = {
  gasPrice: '0x3B9ACA00',
  from : deployerAddress,
  gas: '7500000', // This is an optional parameter, if not passed gas will be estimated.
};
let organizationContractInstance;
let organizationAddress;
Organization.setup(web3Provider, orgConfig, txOptions).then(function(instance){
  organizationContractInstance = instance;
  organizationAddress = organizationContractInstance.address;
});

Deploy EIP20Token contract

To perform economy setup, you will need an EIP20Token. You can either use an existing EIP20Token contract or deploy a new one.

Deploy TokenRules contract

One TokenRules contract is deployed per Organization. Only the Organization whitelisted workers can register rule contracts in the TokenRules contract.

txOptions = {
  from: deployerAddress,
  gasPrice: gasPrice,
  gas: gas
};
const tokenRulesSetupObject = new OpenST.Setup.TokenRules(web3Provider);
let tokenRulesAddress;
tokenRulesSetupObject.deploy(organization, eip20Token, txOptions).then(function(response){
  tokenRulesAddress = response.receipt.contractAddress;
})

Deploy TokenHolder MasterCopy

txOptions = {
  from: deployerAddress,
  gasPrice: gasPrice,
  gas: gas
};
const userSetup = new OpenST.Setup.User(web3Provider);
let tokenHolderMasterCopyAddress;
userSetup.deployTokenHolderMasterCopy(txOptions).then(function(response){
  tokenHolderMasterCopyAddress = response.receipt.contractAddress;
});

Deploy Gnosis MasterCopy

txOptions = {
  from: deployerAddress,
  gasPrice: gasPrice,
  gas: gas
};
const userSetup = new OpenST.Setup.User(web3Provider);
let gnosisMasterCopyAddress;
userSetup.deployMultiSigMasterCopy(txOptions).then(function(response){
  gnosisMasterCopyAddress = response.receipt.contractAddress;
});

Deploy DelayedRecoveryModule MasterCopy

txOptions = {
  from: deployerAddress,
  gasPrice: gasPrice,
  gas: gas
};
const userSetup = new OpenST.Setup.User(web3Provider);
let delayedRecoveryModuleMasterCopyAddress;
userSetup.deployDelayedRecoveryModuleMasterCopy(txOptions).then(function(response){
  delayedRecoveryModuleMasterCopyAddress = response.receipt.contractAddress;
});

Deploy CreateAndAddModules Contract

txOptions = {
  from: deployerAddress,
  gasPrice: gasPrice,
  gas: gas
};
const userSetup = new OpenST.Setup.User(web3Provider);
let createAndAddModulesAddress;
userSetup.deployCreateAndAddModules(txOptions).then(function(response){
  createAndAddModulesAddress = response.receipt.contractAddress;
});

Deploy UserWalletFactory Contract

txOptions = {
  from: deployerAddress,
  gasPrice: gasPrice,
  gas: gas
};
const userSetup = new OpenST.Setup.User(web3Provider);
let userWalletFactoryAddress;
userSetup.deployUserWalletFactory(txOptions).then(function(response){
  userWalletFactoryAddress = response.receipt.contractAddress;
});

Deploy ProxyFactory Contract

txOptions = {
  from: deployerAddress,
  gasPrice: gasPrice,
  gas: gas
};
const userSetup = new OpenST.Setup.User(web3Provider);
let proxyFactoryAddress;
userSetup.deployProxyFactory(txOptions).then(function(response){
  proxyFactoryAddress = response.receipt.contractAddress;
});

Deploy PricerRule Contract

txOptions = {
  from: deployerAddress,
  gasPrice: gasPrice,
  gas: gas
};
const rulesSetup = new OpenST.Setup.Rules(web3Provider);
const baseCurrencyCode = 'OST';
const conversionRate = 10;
const conversionRateDecimals = 5;
const requiredPriceOracleDecimals = 18;
let pricerRuleAddress;
rulesSetup.deployPricerRule(baseCurrencyCode, conversionRate, conversionRateDecimals, requiredPriceOracleDecimals, txOptions).then(function(response){
  pricerRuleAddress = response.receipt.contractAddress;
})

Registration of Rule to TokenRules

txOptions = {
  from: worker,
  gasPrice: gasPrice,
  gas: gas
};
const pricerRule = 'PricerRule';
const tokenRules = new OpenST.Helpers.TokenRules(tokenRulesAddress, web3Provider);
const pricerRuleAbi = abiBinProvider.getABI(pricerRule);
tokenRules.registerRule(pricerRule, pricerRuleAddress, pricerRuleAbi.toString(), txOptions).then(function(response){
  console.log("Successfully registered PricerRule");
})

Creating a User Wallet

txOptions = {
  from: deployerAddress,
  gasPrice: gasPrice,
  gas: gas
};
const User = new OpenST.Helpers.User(
  tokenHolderMasterCopyAddress, 
  gnosisMasterCopyAddress, 
  delayedRecoveryModuleMasterCopyAddress, 
  createAndAddModulesAddress, 
  eip20Token, 
  tokenRulesAddress,
  userWalletFactoryAddress, 
  proxyFactoryAddress, 
  web3Provider
);
const owner = '0xaabb1122....................';
const ephemeralKey = '0xaabb1122....................';
const sessionKeySpendingLimit = '1000000';
const sessionKeyExpirationHeight = '100000000000'; 
const threshold = 1;
const recoveryOwnerAddress = '0xaabb1122....................';
const recoveryControllerAddress = '0xaabb1122....................';
const recoveryBlockDelay = 10;
let gnosisSafeProxy;
let userTokenHolderProxy;
User.createUserWallet(
  [owner], 
  threshold, 
  recoveryOwnerAddress, 
  recoveryControllerAddress, 
  recoveryBlockDelay, 
  [ephemeralKey], 
  [sessionKeySpendingLimit], 
  [sessionKeyExpirationHeight], 
  txOptions
).then(function(response){
  const returnValues = response.events.UserWalletCreated.returnValues;
  const userWalletEvent = JSON.parse(JSON.stringify(returnValues));
  gnosisSafeProxy = userWalletEvent._gnosisSafeProxy;
  userTokenHolderProxy = userWalletEvent._tokenHolderProxy;
});

Creating a Company Wallet

txOptions = {
  from: deployerAddress,
  gasPrice: gasPrice,
  gas: gas
};
const User = new OpenST.Helpers.User(
  tokenHolderMasterCopyAddress,
  null, // gnosis safe master copy address
  null, // delayed recovery module master copy address
  null, // create and add modules contract address
  eip20Token,
  tokenRulesAddress,
  userWalletFactoryAddress,
  proxyFactoryAddress,
  web3Provider
);
const owner = '0xaabb1122....................';
const ephemeralKey = '0xaabb1122....................';
const sessionKeySpendingLimit = '1000000';
const sessionKeyExpirationHeight = '100000000000'; 
let companyTokenHolderProxy;
User.createCompanyWallet(
  owner,
  [ephemeralKey],
  [sessionKeySpendingLimit],
  [sessionKeyExpirationHeight],
  txOptions
).then(function(response){
  const returnValues = response.events.ProxyCreated.returnValues;
  const proxyEvent = JSON.parse(JSON.stringify(returnValues));
  companyTokenHolderProxy = proxyEvent._proxy;
});

Direct transfer of utility tokens

const txOptions = {
  from: relayer,
  gasPrice: gasPrice,
  gas: gas
};
const tokenRules = new OpenST.Helpers.TokenRules(tokenRulesAddress, web3Provider);
const receiver = '0xaabb1122....................'; 
const directTransferCallData = tokenRules.getDirectTransferExecutableData([receiver], [100]);
const nonce = 0; // Ephemeral key current nonce.
const tokenHolder = new OpenST.Helpers.TokenHolder(web3Provider, userTokenHolderProxy);
let transaction = {
  from: userTokenHolderProxy,
  to: tokenRulesAddress,
  data: directTransferCallData,
  nonce: nonce,
  callPrefix: tokenHolder.getTokenHolderExecuteRuleCallPrefix(),
  value: 0,
  gasPrice: 0,
  gas: 0
};
let ephemeralKeyAccountInstance; // Construct account instance of ephemeral key
const vrs = ephemeralKeyAccountInstance.signEIP1077Transaction(transaction);
tokenHolder.executeRule(tokenRulesAddress, directTransferCallData, nonce, vrs.r, vrs.s, vrs.v, txOptions).then(function(response){
  console.log("Successful transfer done!");
})

Setup of PriceOracle

Make sure PriceOracle contract is already deployed. Refer PriceOracle npm for setup of PriceOracle contract.

Payment through PricerRule contract

async function pay() {
   let txOptions = {
      from: worker,
      gasPrice: gasPrice,
      gas: gas
   };
  const pricerRule = new OpenST.Helpers.Rules.PricerRule(web3Provider, pricerRuleAddress);
  let priceOracleAddress;
  const acceptanceMargin = '10000000000000000000';
  await pricerRule.addPriceOracle(priceOracleAddress, txOptions); // Set PriceOracle address in PricerRule.
  await pricerRule.setAcceptanceMargin('USD', acceptanceMargin, txOptions); // Set appropriate acceptance margin.
       
  txOptions = {
    from: relayer,
    gasPrice: gasPrice,
    gas: gas
  };
  const tokenHolder = new OpenST.Helpers.TokenHolder(web3Provider, userTokenHolderProxy);
  const receiver = '0xaabb1122....................'; 
  const nonce = 0; // Ephemeral key current nonce.
  let baseCurrencyIntendedPrice; // Current OST/USD price.
  const pricerRulePayCallData = pricerRule.getPayExecutableData(userTokenHolderProxy, [receiver], ['1000'], 'USD', baseCurrencyIntendedPrice);
  let transaction = {
    from: userTokenHolderProxy,
    to: pricerRuleAddress,
    data: pricerRulePayCallData,
    nonce: nonce,
    callPrefix: tokenHolder.getTokenHolderExecuteRuleCallPrefix(),
    value: 0,
    gasPrice: 0,
    gas: 0
  };
  let ephemeralKeyAccountInstance;
  const signatureObj = ephemeralKeyAccountInstance.signEIP1077Transaction(transaction);
  tokenHolder.executeRule(pricerRuleAddress, pricerRulePayCallData, nonce, signatureObj.r, signatureObj.s, signatureObj.v, txOptions).then( function() {
    console.log("Successful transfer done!");
  })
}
pay();

Wallet Operations

Add Wallet

async function addWallet() {
    const txOptions = {
      from: relayer,
      gasPrice: gasPrice,
      gas: gas
    };
    const gnosisSafe = new Package.Helpers.GnosisSafe(gnosisSafeProxy, web3Provider);
    const threshold = 1;
    const ownerToAdd = '0xaabb1122....................';
    const ownerToAddWithThresholdCallData = gnosisSafe.getAddOwnerWithThresholdExecutableData(ownerToAdd, threshold);
    const nullAddress = '0x0000000000000000000000000000000000000000';
    const nonce = await gnosisSafe.getNonce();
    const safeTxData = await gnosisSafe.getSafeTxData(gnosisSafeProxy, 0, ownerToAddWithThresholdCallData, 0, 0, 0, 0, nullAddress, nullAddress, nonce);
    // 2. Generate EIP712 Signature.
    const signatureObj = await owner.signEIP712TypedData(safeTxData);
    await gnosisSafe.execTransaction(gnosisSafeProxy, 0, ownerToAddWithThresholdCallData, 0, 0, 0, 0, nullAddress, nullAddress, signatureObj.signature, txOptions);
};
addWallet();    

Similar above steps apply for removeWallet and replaceWallet operations.

Authorize Session

async function authorizeSession() {
    const txOptions = {
      from: relayer,
      gasPrice: gasPrice,
      gas: gas
    };
    const tokenHolder = new TokenHolder(web3Provider, userTokenHolderProxy);
    const sessionKeyToAuthorize = '0xaabb1122....................';
    const spendingLimit = '1000000000000';
    const expirationHeight = '100000000000';
    const nullAddress = '0x0000000000000000000000000000000000000000';
    const authorizeSessionCallData = tokenHolder.getAuthorizeSessionExecutableData(sessionKeyToAuthorize, spendingLimit, expirationHeight);
    const gnosisSafe = new Package.Helpers.GnosisSafe(gnosisSafeProxy, web3Provider);
    const nonce = await gnosisSafe.getNonce();
    const safeTxData = gnosisSafe.getSafeTxData(userTokenHolderProxy, 0, authorizeSessionCallData, 0, 0, 0, 0, nullAddress, nullAddress, nonce);
    const signatureObj = await owner.signEIP712TypedData(safeTxData);
    await gnosisSafe.execTransaction(userTokenHolderProxy, 0, authorizeSessionCallData, 0, 0, 0, 0, nullAddress, nullAddress, signatureObj.signature, txOptions);
};
authorizeSession(); 

Similar above steps apply for revokeSession and logout operations.

ABI and BIN provider

openst.js comes with an abi-bin provider for managing abi(s) and bin(s).

The abiBinProvider provides abi(s) and bin(s) for the following contracts:

  • TokenHolder (TokenHolder contract deployed on UtilityChain)
  • TokenRules (TokenRules contract deployed on UtilityChain)
  • PricerRule (PricerRule is a rule contract deployed on UtilityChain)
  • GnosisSafe (MultiSignature wallet with support for confirmations using signed messages)
  • DelayedRecoveryModule (Allows to replace an owner without Safe confirmations)
  • CreateAndAddModules (Allows to create and add multiple module in one transaction)
  • UserWalletFactory (Creates proxy for GnosisSafe, TokenHolder and DelayedRecoveryModule)
// Fetching ABI examples.
let abiBinProvider = new OpenST.AbiBinProvider();
const TokenHolderAbi = abiBinProvider.getABI('TokenHolder');

Tests

Tests require docker-compose. To run the tests, execute below command from root directory.

    // For unit tests.
    npm run test
    // For integration tests.
    npm run test:integration

openst.js's People

Contributors

0xsarvesh avatar abhayks1 avatar benjaminbollen avatar gulshanvasnani avatar kedarchandrayan avatar schemar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

openst.js's Issues

Recovery setup: deploy Recovery master copy and integrate with AddUser

Below are the steps:

  • Deploy Delayed Recovery MasterCopy in UserSetup: lib/setup/User.js.
  • Deploy proxy for "Delayed Recovery contract". The method can go to lib/helper/User.js.
  • Integrate Recovery module with createUserWallet method. Below how the current createUserWallet looks like:
 createUserWallet(
    owners,
    threshold,
    to,
    data,
    sessionKeys,
    sessionKeysSpendingLimits,
    sessionKeysExpirationHeights,
    txOptions
  )

to, data argument of createUserWallet needs to integrate with delayed Recovery module.

PricerRule Interaction methods

Below methods should be implemented in lib/helper/rules module

  • addPriceOracle
  • removePriceOracle
  • setAcceptanceMargin
  • removeAcceptanceMargin

Align repo as per .eslint config

Replace .eslint config with the following:

{
    "extends": [
        "airbnb-base"
    ],
    "rules": {
        "no-console": "off",
        "no-underscore-dangle": "off",
        "import/no-extraneous-dependencies": "off",
        "strict": "off"
    },
    "env": {
        "mocha": true,
        "node": true,
        "es6": false
    },
    "globals": {
        "artifacts": false,
        "contract": false,
        "assert": false
    }
}

Update JS accordingly.

User Setup: Company to User

In this case instead of multisig master copy address, hardware address will be needed.

Ben Inputs:
At the interaction layer we should have a createCompanyWallet which only deploys the TokenHolder (not Multisig or recovery module) for company.
and it can use the standard ProxyFactory for that.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.