Skip to content

Native IOS Mobile Reference Application For DAO/ReFi Management

License

Notifications You must be signed in to change notification settings

MitchTODO/Crypta

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

App Pic

Swift 5 SwiftUI web3 License

Crypta

Crypta is a reference iOS app allowing for en easy integration into web3 projects. For demonstration the app manages a DAO smart contract hosted on the Alfajores Testnet.



About

This project is a submission for an Gitcoin bounty by the Celo Network. The goal was to develop a native iOS reference application to inspire web3 projects.

Requirements

  • iOS 15.2+
  • Xcode 13.2.1+
  • Swift 5+
  • Cocoapods

Quick Start

1. Open your terminal and Git clone the repo

   git clone https://github.com/MitchTODO/Crypta.git

2. Install Podfile

  • With the same terminal cd into Crypta2.0

    cd Crypta/Crypta2.0
    
  • Install pods

    pod install
    

    If your running on M1 chip use

    arch -x86_64 pod install
    

    Wait for the pods to install

3. Start Xcode and open up the workspace

  • Launch Xcode and select Open a existing project then select the Crypta2.0 workspace.

    Note: You might need to wait for Xcode to process files

  • Build and launch the app.

4. Send Funds to the app

When launching for the first time, you will be prompted to create a new password. The password will be used to generate a wallet. When completed you will be navigated to the GroupsView.

Note: You will need your password throughout the app to sign/send transactions.

Before creating groups and voting you must add some liquidity to the app. This will be used to pay for the gas price associated with writing to the smart contract (ie creating managing groups, proposal and voting).

  1. Navigate over the profile view then press the qr code this will copy your address.

  2. Then with google go to Alfajores Testnet Faucet and paste your address; then press to send. This will send 1.0 in Celo,cUSD and cEUR.

  3. Press refresh on the app and watch balance be updated.

5. Using the app

You can now participate in create groups, proposals and voting.


Integration into another contract

Components

App has a simplistic and cookie cutter design that can be broken down into three sections Contract, Services and Views.

  • Contract: Variables that make up the contract, network and wallet.

  • Services: Classes that extend upon the web3swift library.

    • Web3Services: Class contains async functions used to read and write data from the contract. Also initializes the web3 object to facilitate network settings and keystoreManager.
    • KeyStoreServices: Class for creating and saving the key store (Wallet).
    • WebSockets: Class for connecting and subscribing to contract events (logs).
  • Views: SwiftUI views and viewModels that make up the UI.


Setting up contract, network and tokens variables

Contract Address

When a contract deployment was successful, it will output the contract address.

// Crypta2.0/Contract/Address.swift
// Change to your contract address
let contractAddress = "0xa83453C7fB2D22EbA5d87080C76Ba8fb810349f5"

Truffle deployment example App Pic

Contract ABI

The ABI allows another program to interact with the contract. Basically a list of all the variables and functions, inputs and outputs that the contract has available. The app needs this to communicate with the contract. The ABI below was copied from the DAO.json file that Truffle creates in the build folder.

Note ABI's for deployed contracts can be found on https://etherscan.io/ via contract address

// Crypta2.0/Contract/ABI.swift
// Remove the existing ABI and paste the ABI from your contract build folder.
let contractABI =
"""
[
    {
      "anonymous": false,
      "inputs": [
        {
          "indexed": false,
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "name": "GroupActivated",
      "type": "event"
    },
    ...
"""

Network

Only two parameters are needed to create a network chainId and rpcEndpoint. Both should be available on the networks website.

Celo Network docs

For web sockets paste the wss url.

// Crypta2.0/Contract/ABI.swift
// Add your networks
let yourNet = Network(chainId: BigUInt(<Your ChainId>),rpcEndpoint: <"Your RPC endpoint">)
let webSocketURI = "<uri>"


let alfajoresTestnet = Network(chainId: BigUInt(44787) , rpcEndpoint: "https://alfajores-forno.celo-testnet.org")
let alfajoresTestnetSocket = "wss://alfajores-forno.celo-testnet.org/ws"

Contract Methods

Optional

Enum containing strings of all callable methods within the contract. This will make it easier when calling contract methods.

If your not sure what methods are callable copy and paste your contract into https://remix.ethereum.org/

// Crypta2.0/Contract/Methods.swift
// Replace with your contract methods
enum ContractMethods:String {
    case createProposal = "createProposal"
    case getProposalCount = "getProposalCount"
    case getProposals = "getProposals"
    case getProposal = "getProposal"
    case getChoice = "getChoice"
    case vote = "vote"
    case getVote = "getVote"
    case removeVote = "removeVote"
    case newGroup = "newGroup"
    case getGroup = "getGroup"
    case disableGroup = "disableGroup"
    case activateGroup = "activateGroup"
    // This is a public variable not method but still callable
    case groupIdTracker = "groupIdTracker"
}

Contract Logs

Optional

Enum containing log topics emitted from the contract. Used in WebSockets to id the events.

More info on logs,topics and events

// Crypta2.0/Contract/Events.swift
// Replace with your contract topics
enum Topics:String {
    case newProposal = "0xfcf3b1aa65a464cef2889608f99e8b8c0f680a4be6c2acb9d961c536a5a9294b"
    case newGroup = "0xf0adfb94eab6daf835deb69c5738fe636150c3dfd08094a76f39b963dc8cb05a"
}

Easy way to get topics is to used https://remix.ethereum.org/ and look at the log within the transaction. Log Pic

Tokens

Optional

Tokens that will be sent from the app. Only needed if you are planning to send/receive tokens. Important part is the token address and amount of decimals.

// Crypta2.0/Contract/Tokens.swift
// Replace with your tokens
let CELO = ERC20Token(name: "Celo Native Asset", address: "0xF194afDf50B03e69Bd7D057c1Aa9e10c9954E4C9", decimals: "18", symbol: "CELO")
let cUSD = ERC20Token(name: "Celo Dollar", address: "0x874069fa1eb16d44d622f2e0ca25eea172369bc1" , decimals: "18", symbol: "cUSD")
let cEUR = ERC20Token(name: "Celo Euro", address: "0x10c892a6ec43a53e45d0b916b4b7d383b1b78c0f", decimals: "18", symbol: "cEUR")
let cREAL = ERC20Token(name: "REAL", address: "0xC5375c73a627105eb4DF00867717F6e301966C32", decimals: "18", symbol: "cREAL")

Views

Depending on what you are building the UI will be different.

Here is the view hierarchy / Flow diagram, try to find what you need and start from there.I recommend building within the contentView. As this is where you end up after a successful login.

Check out Stewart video on the loginView https://www.youtube.com/watch?v=QrTChgzseVk&ab_channel=StewartLynch

Log Pic

Async Calls

Calling a contract method from views. Here are two different examples.

task

Easy and simple, but only works with swiftUI 15.0+ Check out the Apple docs on .task

// Crypta2.0/Views/BalanceView.swift
.task {

          Web3Services.shared.readContractMethod(contractAddress: token.address, contractABI: Web3.Utils.erc20ABI, method: "balanceOf", parameters: params) { result in
              DispatchQueue.main.async { [self] in
                  switch(result){
                  case .success(let result):
                      let balanceBigUInt = result["0"] as! BigUInt
                      let balanceString = Web3.Utils.formatToEthereumUnits(balanceBigUInt, toUnits: .eth, decimals: 3)!
                      balance = balanceString
                  case .failure(let error):
                      self.error = Web3Services.Web3ServiceError(title: "Failed to get balance.", description: error.errorDescription)
                  }
              }
          }
      }

ViewModels

More complex, but works with order versions and storyboard.

// Crypta2.0/Views/ProposalViewModel.swift
func createProposal(groupId:BigUInt,proposal:Proposal,choiceOne:String,choiceTwo:String ,password:String, completion:@escaping(TransactionSendingResult) -> Void){

    let params = [groupId,proposal.title,proposal.description,startTime,endTime,[[0,choiceOne],[0,choiceTwo]]] as [AnyObject]
    // Make call with shared instance
    Web3Services.shared.writeContractMethod(method: .createProposal, params: params, password:password ) {
        result in
        // Update UI on main thread
        DispatchQueue.main.async { [unowned self] in
            showProgress = false
            switch(result) {
            case .success(let tx):
                completion(tx)
            case .failure(let txError):
                self.error = Error(description: txError.errorDescription)
            }
        }
    }
}

WebSockets

WebSockets is still a work in progress and changes will need to be done in the handleEvent function. Currently it updates two published variables. BannerView is binded to the variables and displayed when a new event received.

Note: The Web3swfit library is very limited when it comes to encoding/decoding log data.

@ObservedObject var webSocket = WebSockets()
var body: some View {
    ZStack{

        BannerView(show: $webSocket.newEvent, title: $webSocket.eventTitle).zIndex(1)
// MARK: handleEvent
/// TODO: Decode the log data
func handleEvent(message:SocketMessage) {
    
    // Using topic to id the event type
    switch(message.params.result.topics.first) {

    case Topics.newProposal.rawValue:
        newEvent = true // Only toggle if we know the event 
        eventTitle = "A new proposal has been created."

    case Topics.newGroup.rawValue:
        newEvent = true
        eventTitle = "A new group has been created."
    default:
        print("Unkown Topic")

    }
}

Phone on the right creates a new group. Phone on the left receives the event.


Resources

web3swift docs

Celo docs

Smart Contract

The contract portion will be rolled into a project of its own.

As of now the contract is deployed on Alfajores Testnet and the ABI is still the same.

  Address: 0xBF27b48c9Cc931A40Dfa995C71D4769215C0b3a3

Contributing

Know of a better way? I'm all ears! Just remember this project was created to be as cookie cutter as possible.

Licence

The project is available under MIT licence

About

Native IOS Mobile Reference Application For DAO/ReFi Management

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published