# Sensei Stake - Workflow
## Glosary
* FAC (contract factory): it is the main entrypoint for a user to deposit/withdraw ethers. It has the hability to create more SC.
* SC (service contract): has a 1-1-1 relationship to a validator and thus an NFT. It is where the ethers deposited from a user goes to.
* NFT (non fungible token): it provides a proof of ownership and liquidity of a validator.
* Alice: a client.
* Strapi: backend where required deployment info is stored.
* Salt: a random 32bytes value that translates to a SC address
* AliceAddress: metamask user alice wallet address.
* Ethers: provider (RPC node)
## DAPP
**Status**: currently only works in `goerli testnet`.
### First Step: Deposit
```sequence
Alice->Strapi: 1. GET /service-contracts
Strapi->Alice: [salts]
Note left of Strapi: should contain a \n list of 10 values
Alice->FAC: 2. fundMultipleContracts([salts], {value: 32/64 ETH})
FAC->Alice: receipt
Note left of FAC: if success returns in logs \n a list of service contracts \n addresses that were deposited to. \n Needed because list of salts \n provided would be arround 10 \n and the effective deposit will be \n in 1/2 service contract \n (it depends if deposited 32/64 ETH)
Alice->Strapi: 3. POST /service-contracts/deposit
Note right of Alice: payload: { userWallet, serviceContracts } \n serviceContracts: list of SC addresses deposited to. \n userWallet: AliceAddress
```
### Second Step: Create Validator (mint NFT)
```sequence
Alice->FAC: 1. getDepositServiceContract(AliceAddress)
FAC->Alice: [SC addresses]
Note left of FAC: list of SC addresses user deposited to.\n Always discard first value (0x0 address)
Alice->FAC: 2. getDepositsAt(SC address, AliceAddress) \n(FOR_EACH returned SC address in 1)
FAC->Alice: amount deposited of AliceAddress in SC address
Alice->Ethers: 3. getBalance(SC address) \n(FOR_EACH returned SC address in 1)
Ethers->Alice: address balance
Alice->Strapi: 4. POST /validators/getCreateValidatorPayload \n(if 3 returned > 0)
Note right of Alice: POST payload: { userWallet, serviceContracts } \n serviceContractAddress: SC address deposited to. \n userWallet: AliceAddress
Strapi->Alice: {validatorPubKey, depositSignature, \ndepositDataRoot, exitDate}
Alice->SC: 5. createValidator(validatorPubKey, depositSignature, \ndepositDataRoot, exitDate)
SC->Alice: receipt
Note right of Alice: if success go to 6
Alice->Strapi: 6. GET /validators/createValidator?validatorPubKey=${validatorPubKey}
Note right of Alice: validatorPubKey is a response from 4
```
### Optional Step: Withdraw
A witdrawal can be made if user has deposited, SC has balance in it and SC is in one of the two states:
* PreDeposit: validator not created yet, if user withdraws in this state service contract can (and should) be reutililized for another user.
* Withdrawn: validator was created, and stopped its operation in beaconchain (called withdraw function in deposit contract), and called `endOperatorServices()` function in SC. SC cannot be reutilized any more, it's lifecycle is done.
```sequence
Alice->FAC: 1. getWithdrawalAllowance()
FAC->Alice: allowed_withdrawal_amount
Alice->FAC: 2. getDepositServiceContract(AliceAddress)
FAC->Alice: [SC addresses]
Note left of FAC: list of SC addresses user deposited to.\n Always discard first value (0x0 address)
Alice->FAC: 3. withdrawAll()
FAC->Alice: receipt
Note left of FAC: if receipt success do 4, otherwise ends here
Alice->Strapi: 4. GET /service-contracts/putInQueue/${SC address} \n(FOR_EACH returned SC address in 2)
Note right of Alice: 4 is only executed if SC address state == 'PreDeposit'
```
A slight change of this sequence diagram occurs if `allowed_withdrawal_amount < total_amount_deposited`. In this scenario after executing 3 an `increaseWithdrawalAllowance()` should be made:
```sequence
Alice->FAC: 2.1. increaseWithdrawalAllowance(total_amount_deposited)
FAC->Alice: receipt
```
## Strapi
Al desplegar los contratos inicialmente se almacenan en strapi:
* /erc-721: address del nft token contract
* /contract-factory: address del contract factory
* /service-contract: toda la data del despliegue de cada service contract (como por ejemplo keystore, salt, etc.)
Ademas hay content types necesarios para utilizacion de la dapp:
* /validator: determina el estado del deposito del cliente. Si esta en pending es porque deposito pero no dio en createValidator() en el service contract.
* /uploaded-keystores: donde fue subido el keystore del service contract (y si lo fue)
### Security related
* GET /service-contracts: solamente retorna los service contracts que esten en cola (aquellos que no hayan sido depositado previamente). Y solo muestra la data necesaria al frontend:
* id, salt, serviceContractAddress
* POST /service-contracts/deposit: en el flujo normal de la dAPP se llama luego de que se confirma por frontend que un deposito entro correctamente al smart contract. No obstante, tambien se checkea por backend que ese la transaccion haya sido exitosa :smiley:. Luego de confirmarlo en la blockchain, se crea un registro en `validator` con el estado "Pending".
* GET /service-contracts/putInQueue/:address: luego de hacer un retiro, y el estado ser "PreDeposit" volver a ponerlo en la cola, y borrar validator record.
* POST /validators/getCreateValidatorPayload: solamente te retorna los valores si el usuario que esta pidiendo esto tiene deposito realizado y confirmado en blockchain. Esto de todos modos no es necesario, los datos son relativamente publicos:
* validatorPubKey: contract.validatorPubKey,
* depositSignature: contract.depositSignature,
* depositDataRoot: contract.depositDataRoot,
* exitDate: contract.exitDate
* GET /validators/createValidator: solamente realiza acciones si realmente realizo el deposito (explicado en detalle mas abajo en seccion [Cron jobs](#Cron-jobs) punto 1)
### Cron jobs
1. Every 1 minute: `validator.services.verifyCreateValidator` sanitizador de registros de `validator` strapi: busca todos los registros de `validator` cuyo estado sea "Pending" (esto sucede si hay un deposito hecho) y verifica en blockchain si el estado del service contract es "PostDeposit" es decir que se llamo a createValidator(). Si sucede esto tenemos que actualizar el registro de `validator` de "Pending" a "Active" y se notifica al discord.
* NOTA: esto solo sucede si el usuario le da en createValidator() desde la dAPP y se le cae internet, o cierra o algo, inmediatamente luego de hacer click en confirmar en metamask. Aca no realizaria el ultimo paso detallado en [Second Step](#Second-Step:-Create-Validator-(mint-NFT)) paso 6.
2. Every 5 minutes: `validator.services.checkPending` alertas a discord de deposito en service contract idle, como para avisar luego que tiene que crear validador porque no esta generando rewards.
3. TODO: agregar sanitizador de service-contracts que verifique aquellos que no estan onQueue, ver si no tienen depositos hechos y estan en estado "PreDeposit". Si sucede esto, hay que ponerlo en cola nuevamente.