{"id":30064662,"url":"https://github.com/celo-org/social-connect","last_synced_at":"2025-08-08T05:17:23.757Z","repository":{"id":197233725,"uuid":"697681440","full_name":"celo-org/social-connect","owner":"celo-org","description":"Protocol mapping social identifiers to blockchain addresses","archived":false,"fork":false,"pushed_at":"2025-08-06T18:39:20.000Z","size":104851,"stargazers_count":8,"open_issues_count":17,"forks_count":10,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-08-06T20:38:47.992Z","etag":null,"topics":["attestation","privacy"],"latest_commit_sha":null,"homepage":"https://socialconnect.xyz","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/celo-org.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-09-28T08:57:02.000Z","updated_at":"2025-07-28T13:24:20.000Z","dependencies_parsed_at":"2024-08-05T05:25:30.252Z","dependency_job_id":"d443087b-4142-4666-b65b-3a4e020f8534","html_url":"https://github.com/celo-org/social-connect","commit_stats":null,"previous_names":["celo-org/social-connect"],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/celo-org/social-connect","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/celo-org%2Fsocial-connect","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/celo-org%2Fsocial-connect/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/celo-org%2Fsocial-connect/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/celo-org%2Fsocial-connect/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/celo-org","download_url":"https://codeload.github.com/celo-org/social-connect/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/celo-org%2Fsocial-connect/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269367085,"owners_count":24405313,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-08T02:00:09.200Z","response_time":72,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["attestation","privacy"],"created_at":"2025-08-08T05:17:20.785Z","updated_at":"2025-08-08T05:17:23.737Z","avatar_url":"https://github.com/celo-org.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SocialConnect\n\n*For Dev Setup see [CONTRIBUTING.MD](.github/CONTRIBUTING.md)*\n\nSocialConnect is an open source protocol that maps off-chain personal **identifiers** (such as phone numbers, twitter handles, etc.) to on-chain account **addresses**. This enables a convenient and interoperable user experience for use cases such as:\n\n- payments - send money directly to your friend's phone number!\n- social discovery - find someone's account based on their twitter!\n- any other identity applications!\n\nHere is a short demo of a payment from a [Kaala](https://kaala.app/) wallet user to a [Libera](https://medium.com/impactmarket/ready-to-unlock-your-potential-meet-libera-your-new-crypto-wallet-d1053f917b95) wallet user, with only a phone number:\n\n[\u003cimg width=\"800\" alt=\"image\" src=\"https://user-images.githubusercontent.com/46296830/207285114-6ef73be4-10f2-4afc-a066-811e1f3e1042.png\"\u003e](https://www.loom.com/share/8afddd73ba324ec18aeb63fc96d568f9)\n\n## Introduction to SocialConnect\n\n[![Introduction to SocialConnect](https://img.youtube.com/vi/XO_33vb45V4/0.jpg)](https://www.youtube.com/watch?v=XO_33vb45V4)\n\n## 🛠 How it Works\n\nSocialConnect uses a federated model, meaning that anyone has the power to be an **issuer** of attestation mappings. Issuers have the freedom to decide how to verify that the user actually has ownership of their identifier. After verification, issuers register the mapping as an attestation to the [on-chain smart contract registry](https://github.com/celo-org/celo-monorepo/blob/master/packages/protocol/contracts/identity/FederatedAttestations.sol). Attestations are stored under the issuer that registered them. When looking up attestations, we then have to decide which issuers are trusted.\n\nHere are some active issuers verifying and registering attestations:\n\n| Issuer Name | Address                                                                                                         |\n| ----------- | --------------------------------------------------------------------------------------------------------------- |\n| Kaala       | `0x6549aF2688e07907C1b821cA44d6d65872737f05` (mainnet)                                                          |\n| Libera      | `0x388612590F8cC6577F19c9b61811475Aa432CB44` (mainnet) `0xe3475047EF9F9231CD6fAe02B3cBc5148E8eB2c8` (alfajores) |\n\nOff-chain identifiers, originally in plaintext, are obfuscated before they are used in on-chain attestations to ensure user privacy and security. This is done with the help of the [Oblivious Decentralized Identifier Service (**ODIS**)](https://docs.celo.org/protocol/identity/odis). The details of the obfuscation process and how to interact with ODIS are described in the [docs about privacy](docs/privacy.md).\n\n### Want a more profound understanding?\n\nWe've made a mini-series to explain you:\n\n- [Celo Spark: SocialConnect Mini-Series (1/3) — What Is It?](https://www.youtube.com/watch?v=a_756GRPcV4\u0026list=PLsQbsop73cfErtQwacE4WgqQwoVcLvLZS\u0026index=1)\n- [Celo Spark: SocialConnect Mini-Series (2/3) — How Does It Works?](https://www.youtube.com/watch?v=bzZbfoPLYM4\u0026list=PLsQbsop73cfErtQwacE4WgqQwoVcLvLZS\u0026index=2)\n- [Celo Spark: SocialConnect Mini-Series (3/3) — Coding Session](https://www.youtube.com/watch?v=qrIHC496avs\u0026list=PLsQbsop73cfErtQwacE4WgqQwoVcLvLZS\u0026index=3)\n\n## 🧑‍💻 Quickstart\n\nThe following steps use the Celo [ContractKit](https://docs.celo.org/developer/contractkit) to quickly set you up to play around with the protocol. If you would like to use a different library instead, please refer to the [example scripts](examples/).\n\n1. Add the [`@celo/identity`](https://www.npmjs.com/package/@celo/identity) package into your project.\n\n    ```console\n    npm install @celo/identity\n    ```\n\n2. Set up your issuer (read \"Authentication\" section in [privacy.md](docs/docs/privacy.md#authentication)), which is the account registering attestations. When a user requests for the issuer to register an attestation, the issuer should [verify](docs/protocol.md#verification) somehow that the user owns their identifier (ex. SMS verification for phone number identifiers).\n\n    ```ts\n    import { newKit } from \"@celo/contractkit\";\n\n    // the issuer is the account that is registering the attestation\n    let ISSUER_PRIVATE_KEY;\n\n    // create alfajores contractKit instance with the issuer private key\n    const kit = await newKit(\"https://alfajores-forno.celo-testnet.org\");\n    kit.addAccount(ISSUER_PRIVATE_KEY);\n    const issuerAddress =\n        kit.web3.eth.accounts.privateKeyToAccount(ISSUER_PRIVATE_KEY).address;\n    kit.defaultAccount = issuerAddress;\n\n    // information provided by user, issuer should confirm they do own the identifier\n    const userPlaintextIdentifier = \"+12345678910\";\n    const userAccountAddress = \"0x000000000000000000000000000000000000user\";\n\n    // time at which issuer verified the user owns their identifier\n    const attestationVerifiedTime = Date.now();\n    ```\n\n3. Check and top up [quota for querying ODIS](docs/privacy.md#rate-limit) if necessary.\n\n    ```ts\n    import { OdisUtils } from \"@celo/identity\";\n    import { AuthSigner } from \"@celo/identity/lib/odis/query\";\n\n    // authSigner provides information needed to authenticate with ODIS\n    const authSigner: AuthSigner = {\n        authenticationMethod: OdisUtils.Query.AuthenticationMethod.WALLET_KEY,\n        contractKit: kit,\n    };\n    // serviceContext provides the ODIS endpoint and public key\n    const serviceContext = OdisUtils.Query.getServiceContext(\n        OdisContextName.ALFAJORES\n    );\n\n    // check existing quota on issuer account\n    const { remainingQuota } = await OdisUtils.Quota.getPnpQuotaStatus(\n        issuerAddress,\n        authSigner,\n        serviceContext\n    );\n\n    // if needed, approve and then send payment to OdisPayments to get quota for ODIS\n    if (remainingQuota \u003c 1) {\n        const stableTokenContract = await kit.contracts.getStableToken();\n        const odisPaymentsContract = await kit.contracts.getOdisPayments();\n        const ONE_CENT_CUSD_WEI = 10000000000000000;\n        await stableTokenContract\n            .increaseAllowance(odisPaymentsContract.address, ONE_CENT_CUSD_WEI)\n            .sendAndWaitForReceipt();\n        const odisPayment = await odisPaymentsContract\n            .payInCUSD(issuerAddress, ONE_CENT_CUSD_WEI)\n            .sendAndWaitForReceipt();\n    }\n    ```\n\n4. Derive the obfuscated identifier from your plaintext identifier. Refer to documentation on the [ODIS SDK](docs/privacy.md#using-the-sdk) for detailed explanations on these parameters and steps.\n\n    ```typescript\n    // get obfuscated identifier from plaintext identifier by querying ODIS\n    const { obfuscatedIdentifier } =\n        await OdisUtils.Identifier.getObfuscatedIdentifier(\n            userPlaintextIdentifier,\n            OdisUtils.Identifier.IdentifierPrefix.PHONE_NUMBER,\n            issuerAddress,\n            authSigner,\n            serviceContext\n        );\n    ```\n\n5. Register an attestation mapping between the obfuscated identifier and an account address in the `FederatedAttestations` contract. This attestation is associated under the issuer. See [docs](docs/protocol.md#registration) for more info.\n\n    ```typescript\n    const federatedAttestationsContract =\n        await kit.contracts.getFederatedAttestations();\n\n    // upload identifier \u003c-\u003e address mapping to onchain registry\n    await federatedAttestationsContract\n        .registerAttestationAsIssuer(\n            obfuscatedIdentifier,\n            userAccountAddress,\n            attestationVerifiedTime\n        )\n        .send();\n    ```\n\n6. Look up the account addresses owned by an identifier, as attested by the issuers that you trust (in this example only your own issuer), by querying the `FederatedAttestations` contract. See [docs](docs/protocol.md#lookups) for more info.\n\n    ```ts\n    const attestations = await federatedAttestationsContract.lookupAttestations(\n        obfuscatedIdentifier,\n        [issuerAddress]\n    );\n\n    console.log(attestations.accounts);\n    ```\n\n## 🚀 Examples\n\n|                                             Type                                              |\n| :-------------------------------------------------------------------------------------------: |\n|                            [ContractKit](docs/examples/contractKit.ts)                             |\n|                              [EthersJS (v5)](docs/examples/ethers.ts)                              |\n|                                  [web3.js](docs/examples/web3.ts)                                  |\n|         [NextJS based web app (Phone Number)](https://github.com/celo-org/emisianto)          |\n|         [NextJS based templated](https://github.com/celo-org/socialconnect-template)          |\n| [React Native App (Phone Number)](https://github.com/celo-org/SocialConnect-ReactNative-Demo) |\n|      [NextJS based web app (Twitter)](https://github.com/celo-org/SocialConnect-Twitter)      |\n| [Server side NextJS (Twitter)](https://github.com/celo-org/SocialConnect-Twitter-Server-Side) |\n\n\u003c!-- -   [@celo/contractkit](https://docs.celo.org/developer/contractkit) (see [`examples/contractKit.ts`](examples/contractKit.ts)),\n-   [ethers.js](https://ethers.org/) (see [`examples/ethers.ts`](examples/ethers.ts)), and\n-   [web3.js](https://web3js.readthedocs.io/en/v1.8.1/) (see [`examples/web3.ts`](examples/web3.ts)). --\u003e\n\nThe [Runtime Environments section](docs/privacy.md#runtime-environments) shows instructions for using SocialConnect with:\n\n- [NodeJS](https://nodejs.org) (see [Runtime Environments \u003e Node](docs/privacy.md#node)),\n- [React Native](https://www.google.com/url?sa=t\u0026rct=j\u0026q=\u0026esrc=s\u0026source=web\u0026cd=\u0026cad=rja\u0026uact=8\u0026ved=2ahUKEwiK9paNjYH9AhUIesAKHQZ1CvYQFnoECA0QAQ\u0026url=https%3A%2F%2Freactnative.dev%2F\u0026usg=AOvVaw3N725EvNXK2_crezzoIs9d) (see [Runtime Environments \u003e React Native](docs/privacy.md#react-native)), and\n- Web (see [Runtime Environments \u003e Web](privacy.md#web))\n\u003c!--\nThe [emisianto web app](https://emisianto.vercel.app/) is a sample implementation of a phone number issuer. The code is hosted at [celo-org/emisianto](https://github.com/celo-org/emisianto). --\u003e\n\n\u003cimg width=\"500\" alt=\"image\" src=\"https://user-images.githubusercontent.com/46296830/205343775-60e429ea-f5e5-42b2-9474-8ca7dfe842cc.png\"\u003e\n\n## 📄 Documentation\n\nFor a deeper dive under the hood and specific implementation details, check out the documentation of the [protocol](docs/protocol.md) for details on how to interact with the on-chain registry, [privacy](docs/privacy.md) for how identifiers are obfuscated, and [key-setup](docs/key-setup.md) to setup your role keys to interact with the protocol.\n\n## 🤝 Get In Touch\n\nInterested in Integrating SocialConnect, get in touch by filling this [form](https://docs.google.com/forms/d/e/1FAIpQLSeePUyzd2VQfawO8OsXdvmut3OiyICoLPRtfNfPpvtRw3tEfw/viewform).\n\n## 📣 Feedback\n\n**SocialConnect is in beta**! Help us improve by sharing feedback on your experience in the Github Discussion section. You can also open an issue or a PR directly on this repo.\n\n## FAQ\n\n\u003cdetails\u003e\n  \u003csummary\u003eWhat is a \"plainTextIdentifier\"?\u003c/summary\u003e\n\n`plainTextIdentifier` is any string of text that a user can use to identify other user.\n\nPhone number, Twitter handle, GitHub username anything that makes it easier to represent an evm based address.\n\nFor example:- Alice's phone number: `+12345678901`\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eWhat is an \"obfuscatedIdentifier\"?\u003c/summary\u003e\n\nIdentifier that is used on-chain, to which the account address is mapped and used by dApps to lookup. It preserve the privacy of the user by not revealing the underlying `plainTextIdentifier`. Used for on-chain attestations, obtained by hashing the plaintext identifier, identifier prefix, and pepper using this schema: `sha3(sha3({prefix}://{plaintextIdentifier})__{pepper})`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eWhat is an \"identifier prefix\"?\u003c/summary\u003e\n\nIdentifier Prefix is used to differentiate users having same plainTextIdentifier for different purposes and composability.\n\nFor example:- Consider Alice having same username on both Twitter and Github, `alicecodes`.\n\nHow do we differentiate between Alice verified using Twitter and Github? \u003cbr\u003e\nThis where `prefix` comes into play, the `plainTextIdentifier alicecodes` can be represented as `twitter://alicecodes` and `github://alicecodes` this helps differentiate whether Alice was verified using Twitter or Github.\n\nMoreover, it also helps in composability if dApps follow a standard and use prefix then the corresponding `obsfuscatedIdentifier` will be the same thus making it easier for dApps to lookup identifier verified by other issuers.\n\nYou can keep an eye on prefixes suggested by us [here](https://github.com/celo-org/social-connect/tree/main/packages/odis-identifiers/src/identifier.ts#L25).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eWhat is a \"pepper\"?\u003c/summary\u003e\n\n`pepper` is a unique secret, obtained by taking the first 13 characters of the `sha256` hash of the `unblinded signature`\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eWhat is a \"unblinded signature\"?\u003c/summary\u003e\n\nObtained by unblinding the signature returned by ODIS which is the combined output, comprised of signature by ODIS signers.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eWhat is an Issuer?\u003c/summary\u003e\n\nIssuer is an entity that is willing to take the overhead of verifying a user's ownership of an identifier.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eDoes Issuer need to pay for gas?\u003c/summary\u003e\n\nFor lookup there is no requirement for gas, assuming that the `obfuscatedIdentifier` to be used for lookup is available.\n\nFor registering attestations it is optional, once the `obfuscatedIdentifier` is obtained issuer can decide whether to just sign the attestation and provide it back to the user which will then **use its own funds for gas for registering itself** or the `issuer` can perform the transaction which will require the issuer to pay for gas.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eDoes Issuer need to have ODIS quota?\u003c/summary\u003e\n\nYes, Issuer needs to have ODIS Quota to register and lookup users.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eWhat is cost to register a user?\u003c/summary\u003e\n  With 10 cUSD worth of ODIS quota you can attest 10,000 users!\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCan I just lookup users and not register them?\u003c/summary\u003e\n  Yes, you can lookup users under other Issuers. By doing this, you are trusting that the Issuer took care about verifying that the identifier does actually belong to the user.\n\nYou might want to do this if you don't want to create a registry of your own and use an already existing registry created by other Issuers.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003eCan anyone become an Issuer?\u003c/summary\u003e\n  Yes, SocialConnect is open for all. Anyone can become an Issuer\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWhat are some security \u0026 trust assumptions differences between the ASv1 vs. Social Connect?\u003c/summary\u003e\n\n| ASv1                                                    | SocialConnect                                                                                                                    |\n| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |\n| Phone number verified by 3 randomly selected validators | Phone number verified by issuer (no guarantee about authenticity or quality of verification), app developers choose who to trust |\n| Single root of trust = Collective of Validators         | Many roots of trust = Respective attestation issuer that verified phone number                                                   |\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n\u003csummary\u003eWhat's the best way to map an address returned by lookupAttestations to the issuer? \u003c/summary\u003e\n\n```sol\nfunction lookupAttestations(bytes32 identifier, address[] calldata trustedIssuers)\n    external\n    view\n    returns (\n      uint256[] memory countsPerIssuer,\n      address[] memory accounts,\n      address[] memory signers,\n      uint64[] memory issuedOns,\n      uint64[] memory publishedOns\n    )\n```\n\n`lookupAttestations` returns 4 arrays, depending on the order `trustedIssuers` was provided respectively the return values are returned.\n\nFor example:-\n\nif trustedIssuers = [I1, I2, ...]\nthen countsPerIssuer = [CI1, CI2, ...] where CIx = number of accounts attested under the Xth issuer\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eIs there a convention for phone number format?\u003c/summary\u003e\n\nYes, the SDK function `getObfuscatedIdentifier` will only accept E164 formatted phone numbers.\n\n\u003c/details\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcelo-org%2Fsocial-connect","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcelo-org%2Fsocial-connect","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcelo-org%2Fsocial-connect/lists"}