Skip to content

3. Deploy Security Token

Remon Nashid edited this page Sep 22, 2019 · 4 revisions

Token creation form

The form below let's token issuer configure the following token properties:

  • Token Name: a human-friendly name of the token.
  • Token details URL: a URL to a page or documentation with more information about the token.
  • Treasury wallet: Address of a wallet to be used to store tokens for some operations. Defaults to current user (eg Token Issuer) address. Should be a valid ethereum address.
  • Is Divisible: Divisible tokens may be used to represent divisible assets such as bonds. While indivisible tokens are typically used to represent an equity.
function App() {
  const [state, dispatch] = useContext(Store)
  const { sdk } = state.AppReducer
  const form = useForm()
  const { getFieldDecorator, resetFields, validateFields } = form
  // ... previously added effects
  // ...
  return (
    <Form onSubmit={createToken}>
      <Title>Create Your Security Token</Title>

      <Item
        name="symbol"
        label="Reservation">
        {getFieldDecorator('symbol', {
          rules: [{required: true, message: 'A token reservation is required'}],
        })(<Select
          placeholder="Select a reservation">
          {reservations.map(({symbol}) =>
            <Option key={symbol} value={symbol}>{symbol}</Option> )}
        </Select>)}
      </Item>

      <Item
        name="name"
        label="Token Name">
        {getFieldDecorator('name', {
          rules: [{required: true, message: 'Token name is required'}, {max: 64}],
        })(<Input placeholder="Enter Token Name"/>)}
      </Item>

      <Item
        name="detailsUrl"
        label="Token Details">
        {getFieldDecorator('detailsUrl', {initialValue: ''})(<Input placeholder="Paste link here"/>)}
      </Item>

      <Item
        name="treasuryWallet"
        label="Treasury Wallet Address">
        {getFieldDecorator('treasuryWallet', {initialValue: walletAddress,
          rules: [
            { required: true  },
            {
              validator: (rule, value, callback) => {
                // Make sure it's a valid Ethereum address.
                if (!web3Utils.isAddress(value)) {
                  callback('Address is invalid')
                  return
                }
                callback()
                return
              }
            }
          ] })(<Input />)}
      </Item>

      <Item
        name="divisible"
        label="Divisible">
        {getFieldDecorator('divisible', {
          initialValue: false,
          valuePropName: 'checked',
        })(<Switch/>)}
      </Item>

      <Row>
        <Col><Button htmlType="reset" onClick={() => resetFields()}>
        Reset fields
        </Button></Col>
        <Col span={12}> <Button type="primary" htmlType="submit">
          Create my token
        </Button></Col>
      </Row>
    </Form>
  )
}

Blockchain interaction

On form submit, the following function is called. First, the function locates the relevant reservation object amongst other reservations in app state, using submitted symbol (ie values.symbol). Then, it calls reservation.createSecurityToken(values) to create our token.

async function createToken(e) {
  e.preventDefault()
  const fields = ['symbol', 'name', 'detailsUrl', 'treasuryWallet', 'divisible']
  validateFields(fields, { force: true })
    .then(async (values) => {
      dispatch({type: 'CREATING_TOKEN'})
      // Locate reservation object amongst the reservations in app state.
      const reservation = reservations.filter(r => r.symbol === values.symbol)[0]
      // Create security token.
      try {
        const q = await reservation.createSecurityToken(values)
        const ret = await q.run()
        dispatch({ type: 'CREATED_TOKEN'})
        message.success(`Token ${reservation.symbol} has been created successfully!`)
        resetFields()
      }
      catch (error) {
        dispatch({ type: 'ERROR', error: error.message} )
      }
    })
}

As usual, we dispatch a few actions responsible for user feedback (eg spinners and error messages). Those actions are being handled in the reducer cases below.

Reducer

export const reducer = (state, action) => {
  ....
  switch (action.type) {  case 'CREATING_TOKEN':
    return {
      ...state,
      loading: true,
      loadingMessage: 'Creating token'
    }
  case 'CREATED_TOKEN':
    return {
      ...state,
      loading: true,
      reservations: undefined,
      loadingMessage: 'Refreshing reservations',
    }
  ...

Final result

Once all pieces are in place, token creation form should look as follows.

Despite that the SDK summerises blockchain interactions into one method call (ie reservation.createSecurityToken), the SDK will send, and ask you to sign up to 3 transactions:

  • First transaction allows the SecurityTokenRegistry contract to withdraw a predetrmined fee that's required for token issuance (eg 250 Poly).
  • Second transaction creates the token with passed configuration such as token name, URL, and divisibility.
  • On Kovan network, the SDK will check if the current user has enough balance of POLY to conduct token creation transaction. If not, it will ask Kovan's Poly faucet for the necessary amount of Poly.

Note on mainnet Ethereum network you have to ensure a sufficient balance of Poly yourself. Poly can be bought or exchanged on one of these exchanges https://coinmarketcap.com/currencies/polymath-network/#markets.

And Voila! Your token should be created as soon as the transactions above have confirmed! You can view using this simple app https://polymathnetwork.github.io/token-info/