How It's Made
### Tech Stack:
- Languages: [Typescript](https://www.typescriptlang.org/), [Solidity](https://solidity.readthedocs.io/)
- Prod environment: [Firebase](https://firebase.google.com/)
- CI: [Github Actions](https://help.github.com/en/actions)
- API: [Blocknative](https://www.blocknative.com/)
- Libs: [buidler](https://buidler.dev/), [ethers.js](https://docs.ethers.io/ethers.js/html), [telegraf](https://telegraf.js.org)
- DeFi: [Aave](https://aave.com/), [pTokens](https://ptokens.io)
We chose to use **TypeScript** whereas the team was not familiar with it.
At this time, written code is surely not conventional, but choosing it from the beginning, it left us open for some improvement and may bring more interest for potential contributors.
We used **Solidity** for smart contracts because the vast majority of the ecosystem uses it.
For the development of smart-contract, we started from the [starter kit](https://github.com/rhlsthrm/typescript-solidity-dev-starter-kit) communicated at the beginning of the hackathon. So **Buidler** is used, it has been a great experience, and we definitely will continue to use it for future projects.
We also used **ethers.js** in the bot code, to keep the same stack as in the smart contract, and also because it is a good lib and works nicely with **TypeScript**.
For the production environment, **Firebase** was chosen for its convenience as a serverless solution and zero cost for a humble app. Still, we had to upgrade to the Blaze plan because the free (Spark) plan doesn't allow external queries (outside of Google) and we had to make some for notifications. Services used are **Cloud Functions** and **Cloud Firestore**.
For the **Telegram** bot, we used **Telegraf** because it currently seems to be the most active and documented JS library for this purpose.
### Software Architecture
In the beginning, we wanted to create a non-custodial smart wallet (still with some owner related stuff for safety), with encrypted message handling, but our first disappointment was that there is not yet a standard that defines an URI scheme to call smart contract methods, and the ease of use was a priority (we needed to be able to accept the deposit of funds via a QR code). So we decided to create a contract for each onboarding account, the manager being a sort of router, as a result unfortunately, it makes the solution less non-custodial.
On the backend side, there are 2 functions:
- one for the Telegram webhook
- one for the Blocknative webhook
We needed to be notified of transactions happening on the blockchain, so we searched for webhook solutions, but many projects didn't seem to be active anymore, then we found **Blocknative**, it seems to be a young project, and it worked well for us so far. It is a webhook solution that allows to listen to an address and notifications of all the transactions happening.
We store some data in the chose persistence solution (**Cloud Firestore**).
We need to persist data for 2 reasons:
- having a sort of mini-indexer to keep track of the transactions and display a history
- mapping the users with their wallet
As we may expand the solution to other bots in the future, the user is identified with a UID, which is stored in the smart wallet manager.
A sample record of a user data looks like this:
A transaction record like this:
### Security Considerations
The 2 main points of failure are the non-blockchain parts, aka the serverless functions, and the database.
As anyone can query our webhook, we must make sure nobody is faking requests, a first step is to hide our webhook behind a secret path, and change it regularly, we should also verify the origin of each request.
The **Cloud Firestore** database setting must be configured with care, as it is easy to accidentally publicly expose data.
### Smart-contract Architecture
The project uses a `SmartWalletManager` smart-contract, which is managed by the Ethereum private key of the Telegram bot, and serves as the smart-wallet factory. Every time a new user is onboarded on the application, the bot will call the `createWallet(UID)` function on the `SmartWalletManager`, which will then deploy a new smart-contract `UserSmartWallet`, containing all the smart-wallet and DeFi logic and registered with the UID of the newly joined user.
The SmartWalletManager stores a mapping of all the UID with their corresponding smart-wallet addresses (if they exist).
To simplify the listening of events on the Ethereum blockchain and to avoid having the bot to subscribe to every single user smart-wallets, we decided to use the smart-wallet Manager as the central event emitter. Any time an action is processed on a smart-wallet, the latter will send this information back to the smart-wallet manager in order to have only one smart-contract to listen.
Thus, Blocknative will call back the serverless project with a webhook once an event is detected from the Manager contract, and will allow the bot to send a notification message back to the user.
The `UserSmartWallet` smart-contract integrates multiple interfaces from other smart-contracts, more specifically:
- ERC-20 interface to allow the receiving and spending of ERC-20 tokens.
- Aave's `aToken`, `LendingPool` and `LendingPoolAddressesProvider` interfaces to allow the deposit and redemption of lent tokens on the Aave protocol.
- pTokens interface for the `redeem()` method access to swap pBTC on the Ethereum chain into BTC.
The User smart-wallet contract also manages a list of owners that can interact with it.
mapping (address => bool) public hasOwnerAccess;
By default, the Telegram bot FukiPay will be granted owner access to all user smart-wallets. However, the users can add Ethereum addresses to their smart-wallet in order to interact with their smart-wallet from outside the bot (by using a web app connected to the smart-wallet abi). Users can also remove the FukiPay bot owner access, if they want to keep the smart-wallet for themselves, granting complete control of the funds to them.