Git Product home page Git Product logo

daimo's Introduction

Screenshot

Daimo is a stablecoin personal bank

Today, we run on USDC on Base. Cross-chain transfers and more coming soon.

No seed phrases. Keys are generated in your phone's secure enclave and never leave. You can add multiple devices and create Passkey backups to secure your account. Under the hood, it's a ERC-4337 contract wallet.

The mission is to make an excellent experience. Sound assets, secure cryptography, your coins in your control.

FAQ

  • How do Daimo accounts work?

    Daimo accounts are Ethereum accounts.

    Under the hood, they're a new and much-improved type called an ERC-4337 contract account. Each device you add to your account stores a secret key. When you send money, your phone first authenticates you with FaceID or similar, then cryptographically signs the transaction using that key.

    Daimo is non-custodial. Your keys, your coins.

    Daimo offers stronger security than traditional wallets. Keys live in dedicated hardware made for storing secrets, such as Secure Enclave on iPhone, and never leave your device.

  • Which stablecoin does Daimo use?

    Daimo runs on USDC, a high-quality stablecoin issued by Circle.

    Stablecoins are cryptocurrencies designed to maintain a stable value. Many are pegged to the dollar, so that one coin is worth $1. Circle is a US-based licensed money transmitter partnered with Coinbase. USDC is one of the largest and most liquid onchain dollar equivalents. Learn more on Bluechip.

  • Which blockchain does it run on?

    Daimo uses Base, an Ethereum rollup.

    Rollups support near-instant transactions that cost a few cents each. By contrast, transactions on the underlying Ethereum chain (layer 1 or L1) take about 10 times as long and cost a few dollars each. Rollups accomplish this by bundling many transactions into a single L1 transaction. They inherit the strong guarantees of Ethereum: like L1, Base is reliable and secure, and works worldwide. Learn more on L2Beat.

  • Can I send other coins like USDT, or use other chains like Polygon?

    Not yet. We plan to support payments in other stablecoins and on other chains soon.

  • Who can see my Daimo transactions?

    Currently, all Ethereum transactions are generally public, including Daimo transactions. We plan to add private payments as the infrastructure and support for them matures.

  • Is Daimo open source?

    Yes, Daimo is and will always be open-source under GPLv3. We're here to collaborate. We want to make self-custody fast, safe, and easy. See more on our Github.

Security

Audits

Past audits of the Daimo codebase can be found in the audits folder:

Bug Bounty Program

Daimo hosts a bug bounty program on Immunefi. Learn more on SECURITY.md.

Development

Daimo is under active development. Now in App Store and Play Store with an invite code. Coming soon: desktop app, no invite code, cross-chain support.

Architecture

READMEs for each app and package.

Dev quickstart

Clone the repo, loading submodules.

git clone [email protected]:daimo-eth/daimo --recurse-submodules

Build the app.

node --version # ensure you have node 21+
npm i
npm run build

Run the iPhone Simulator in XCode. (If you're not on a Mac, see the Android quick start below.) Get the latest simulator build from Expo; message us if you need access. Drag-drop the build into the simulator to install.

Copy the example .env file to use the remote, hosted API.

cd apps/daimo-mobile
cp .env.example .env

Finally, run the app in the simulator.

npm run dev

Use invite code testnet. Once you create an account, you should automatically get some testnet USDC from the faucet.

Expo apps come in two layers: a native layer and a React Native (Typescript) layer. Whenever you add a native module or update @daimo/expo-enclave, you must rebuild the native app. For details, see apps/daimo-mobile.

Dev quickstart: Android
  • Ensure you have the correct Java version. Version 20 doesn't work, Java 17 works.
  • You need to ANDROID_HOME to the local Android SDK.
  • Install Android Studio, and create an emulator.
  • Download latest Android internal distribution build from Expo, and install it in the emulator.

All other instructions are the same as above. After npm run dev, type a to open the Android simulator. You should now have both side-by-side. See the mobile package.json for details.

Dev quickstart: contracts

Install Foundry.

curl -L https://foundry.paradigm.xyz | bash
# Reload your terminal, then run:
foundryup

Build the contracts.

forge build

For commands to run tests and recompute code coverage, see ci.yml.

Dev quickstart: API

daimo-mobile and daimo-web both rely on daimo-api.

By default:

  • daimo-mobile runs the Expo incremental build server on localhost:8080
  • daimo-web runs the web app, including fallback deeplinks, on localhost:3001
  • daimo-api runs the TRPC API on localhost:3000

You'll need to either use the hosted Daimo API or run one locally.

To run the API locally, fill in .env. Message us if you need help.

You can run Postgres in the background locally using the Mac Postgres app.

Once you're running the API locally, you can run the full stack self-contained.

# First tab
cd packages/daimo-api && npm run dev
# Second tab
cd apps/daimo-mobile && npm run dev
# Third tab
cd apps/daimo-web && npm run dev
Dev quickstart: Maestro

daimo-mobile runs end to end tests with Maestro.

To write or run a test locally, first obtain a Expo build labelled with profile maestro here.

Then, with Maestro installed, you can simply run maestro test <test file name> to run the test. Example: maestro test .maestro/onboardAndRemove.yaml.

In the cloud, these tests are run on the master branch using the maestro-ci workflow in Maestro cloud.

DB diagnostics

Shovel caught up?

SELECT * FROM shovel.latest;

Largest Postgres DB tables, disk usage:

SELECT
  table_schema,
  table_name,
  pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size
FROM
  information_schema.tables t
JOIN
  pg_class c ON c.relname = t.table_name
WHERE table_type = 'BASE TABLE'
ORDER BY pg_total_relation_size(c.oid) DESC
LIMIT 20;

daimo's People

Contributors

andrewliu08 avatar cha0sg0d avatar chescalante avatar dcposch avatar dehli avatar kayleegeorge avatar koredefashokun avatar markusbug avatar michelle-chiu avatar nalinbhardwaj avatar nounderline avatar panieldark avatar pfista avatar ryandotsmith avatar wojtus7 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  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

daimo's Issues

Onboarding polish

  • Make the USDC "learn more here" link go somewhere
  • Link to an explainer about Base

From Byron: consider writing & linking to own explanations. Builds trust w the user. Also can work as content marketing.

Display real fees

  • Call bundler simulation in useSendAsync
  • Display expected fee
  • Set max fee to a reasonable multiple of displayed fee, no more than 2x
  • Warn (and show exact maximum) above a threshold

Remove daimo-userop RPC dependency

Currently, DaimoAccount.init takes a a Viem publicClient.

This is used for exactly one purpose: pulling the latest nonce from the entryPoint contract.

Removing this dependency means that daimo-userop only talks to a bundler RPC, instead of both a bundler RPC and an ethereum JSON RPC.

Means that Daimo overall has two server dependencies--bundler and API--instead of 3. In future, we can reduce this to one by routing everything thru the API and trust-minimizing via light client.

Faucet UX

  • Byron: "what's a faucet"
  • After send, return to home screen

Error boundaries

  • Each screen should have one
  • On the home screen, RecentHistory and the header should have one

Send Request ID, request status

Goal

Show whether a request for payment has been paid.

Current situation

Alice sends Bob a request link for $20.

Bob pays. Alice gets a notification saying Bob paid her $20.

However, payment is not tied to the request onchain. If either of them clicks the link again, it still says "Alice is requesting $20". Bob could accidentally pay the same request twice.

Proposed solution

Add a random bytes32 requestID.

  • Add a RequestLinkScreen. When Bob clicks the link, instead of the generic send screen with amount and recipient prefilled, he sees a Alice is requesting $20 in-app, with a button to pay her.
  • Payment is not a bare USDC transfer. Instead, goes thru Daimo router contract, which emits the requestID as filled.
    • Alternately, attach the requestID as extra data to the userop somehow
  • Either way, indexer now knows which requests have been filled. (Note indexer does NOT have a list of pending, unfilled requests--those are offchain.)

Enable FaceID

From Rafi:

I think changing this line to .biometryAny would result in making onboarding with biometric authentication.

Show pending (outbound) and claimed notes in history

  • Locally track where note was sent.
    • If share sheet is not dismissed but completes successfully, we learn roughly where it went. Save that on the local note. Provides context later from the history screen.
  • Show detailed note status from the history screen.
    • Not only DaimoNoteStatus, but also the locally-saved destination info. If none, show that note was sent from another device. Explain clearly and concisely that a note is like cash, anyone with the link can redeem. Send it to your recipient only, via a secure messenger.

Speed up CI

Was 40s, now 2min

  • Add .npm caching
  • Speed up or parallelize the nextjs build

Sync refactor

Issue

account.ts, sync.ts, and chain.ts are a mess. We need a unified strategy for getting our account balance and history.

Proposal

  • Remove chain.ts entirely. App will not use Ethereum RPC APIs, and instead sync through our own Daimo API.
    • (The goal for the Daimo API is to become untrusted thru the addition of a light client. All important answers will eventually come with proof.)
  • Make a single object (not a network of React hooks) the source of truth for 1. which account we're using 2. balance, 3 history
  • Provide React hooks that only read from this object

Local sync can look something like this

class AccountSyncer {
  // Store account info and history in MMKV
  // Update (sync) via Daimo TRPC API
}

// Replaces ChainContext
// & unlike ChainContext, the provided object is a singleton, never changes 
const SyncContext = createContext<AccountSyncer>();

// Returns account object including address, name, balance, and status 
// (is the balance fresh? are we offline?)
function useSyncAccount() {
}

// Returns a history object including recent ops and contacts.
// Ops include all userops sent from this account (claim name, change key, transfer)
// as well as inbound transfers.
function useSyncHistory() {
}

Touch base (no pun intended)

Hi there,

Just wanted to say hi from Brussels.
We stumbled upon your project and we are also working on a crypto wallet for non crypto people: https://citizenwallet.xyz

So wondering if there could be ways to team up (our plan is to develop this as an open source project like Metamask), or if it makes sense to keep two separate teams to focus on different use cases.

Let's do a call to chat more?
This is our github and our deck

I'm reachable @xdamman on all platforms.

Count, minimize roundtrips

Ping times on mobile are terrible

Download speeds are bad too = minimize API response sizes.

But ping times are especially bad = critical to do every user action in as close as possible to 1 roundtrip.

Google Fi in Paris, hotspot using a recent iPhone:

image

On older androids / outside first-world cities, probably worse.

Handle assets received on other chains

Goal

This is a stub issue to illustrate a problem. No concrete solution yet.

Someone sends 1000 USDC to a Daimo address on a different L2, or on Mainnet.

  • At minimum, user should be notified.
  • We should have some story for how that asset can be recovered.

Refactor: amount / dollars consistency

Goal

  • amount is always in token units, eg 1500000 for USDC
  • dollars is always in dollars, eg 1.50

Proposed details

  • In code, amount should always be bigint. dollars should be string or number.

  • When persisted, both should be string.

  • dollarsToAmount/amountToDollars should round trip accurately, even for amounts greater than MAX_SAFE_INTEGER

  • amountToDollars returns a string with at least 2 decimal points, and as many as needed. (For USDC, up to 6.)

  • amountToDollars(n, 2) returns a string with exactly 2 decimal points

  • Add formatAmountToDollars. This replaces `amount

Recover accounts by signing key

Looking up an account by its signing key allows recovery when a user uninstalls the app and reinstalls.

The Secure Enclave persists signing keys (at least for now, and at least for some period of time after uninstall).

  • Add signing pubkey to account indexer. See router.ts in daimo-api--currently we are just hardcoding a few lookups for testing.
  • Doing this cleanly might require adding a AddSigningKey/RemoveSigningKey event.
  • Add a page in OnboardingScreen.tsx that displays iff there is already a key present in the enclave. In that case, show spinner while the account lookup is occurring, error if API is down, warning if no account found for that key (was this device removed from a Daimo account?), or success if an account was found.

(The lookup is already implemented, just not a UI with error handling. See useLoadAccountFromKey in enclave.ts )

Balance: optimistically reflect pending transactions

Issue

Currently, after sending a transaction, you see a new pending transaction in history but the same, unmodified balance.

Proposed fix

  • Consistent pending state. If the transaction history shows pending transactions, balance should as well.
  • Potential pending indicator. Balance could show 🔘 orange circle when pending, 🟢 when confirmed, etc, similar to history entries.

Request flow

  • Better link
  • Remove hardcoded "dcposch is requesting..."

Index Supply integration

Goal

Fast, scalable access to Daimo account history.

Stopgap

Currently, daimo-api implements an in-memory indexer. Every time the server starts, it loads history using getLogs() with retry and pagination. This should work fine while we have relatively few users and a short activity history.

Index Supply hosted E2PG (chain events to Postgres)

Ryan proposed following 3 options:

  1. Daimo provisions a database and gives Index Supply tightly scoped user access to the database so that we can insert into a predetermined set of tables.
  2. Daimo provisions a database and we configure logical replication so that Index Supply logically replicates data to Daimo’s database. Logical replication is really neat and would allow you to create your own pg indexes on the replicated tables and additionally you can also add columns to the replicated tables so that your app can do app specific things with the data.
  3. Index Supply provisions a (potentially shared) database in your app’s region and provides a read only connection URL. The indexed data will be available < 100ms after the block is added to the chain. I’ve also seen really great performance from our EL/CL stack such that it receives blocks quicker than most public websites on the web.

Option 2 seems clean, so we're trying that first.

For evaluation, we'll just index testUSDC transfers via the standard erc20 integration.

Next steps

  • Draft schema for custom events
    • Account creation/name claimed
    • Add/remove device
    • Create Note/Redeem Note
  • Review schema with Ryan
  • Draft integration PR

Log aggregation

Currently getting logs from render.com, only shows last couple hundred lines

Need logtail or similar

gas optimise sig validation

low hanging fruit: pass the index of the key as call data (maybe just append a byte to the signature?) directly instead of looping the whole list (and eating the computation cost of wrong verifies)

for (uint256 i = 0; i < accountKeys.length; i++) {
if (_sigVerifier.verify(accountKeys[i], prefixedHash, userOp.signature)) {
return 0;
}
}

Support ENS resolution

Goals

  • Send to nalin.eth
  • Send to bare address. Use reverse resolution, show address.+ nalin.eth on confirmation screen if applicable
  • Update screenshots in GH to illustrate Daimo integrates with rest of Ethereum

Onboarding from webapp deep link

Idea

Get a list of people we want to onboard. All we need for each is some kind of messenger--Signal, TG, Whatsapp, or phone number.

Send each a Note. Amounts should be tasteful--$3, $5. We don't want people to feel like they're being bribed. We send just enough that experience receiving USDC and maybe buy a coffee with it.

Super easy onboarding motion.

Track conversion. For 100 sends,

  • How many claim the note?
    • How many claim in Daimo vs other wallet?
      • Of Daimo claims, how many in newly-created account?
        • Onchain D1R: of those, how many take an action the next day?
        • ...

This can double as a dogfooding and benchmarking exercise.

  • However long the list, divide into chunks of 20.
  • Everyone at the company takes a chunk, sends to that list as quickly as possible, timing how long it takes.
  • We record in a google sheet

This is a nice end-to-end test of the app UI transitions + Enclave signing time + send latency. We can scatterplot over time, across phone OS, etc.

Note success state

CreateNoteTab UX improvements

  • Show that the note was created
    • Rn, just shows a disabled "Success" button. No good.
  • Send button.
    • Share sheet can still shows automatically when note is created. Currently, if share sheet is dismissed, you currently end in an unfixable state where your note is lost in the ether. Send button lets you bring it up again.
  • Go to home screen on successful send.
    • Once the share sheet completes successfully, go home. The sent note (potentially pending) should be the top history entry.

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.