Giter Site home page Giter Site logo

api's Introduction

📣🆕 https://api.sorare.com/federation/graphql is the new Sorare API endpoint. It's a federated superset of existing API endpoints so all existing queries remain valid, just replace the URL in your application. Visit the playground.

Sorare API

At Sorare, we are committed to providing an open platform for developers to build upon.

While our Cards are stored on the Ethereum blockchain (or within a Starkware rollup) we support an API that provides more detailed information.

The Sorare API are provided by GraphQL. Sorare provides a federated API for all sports. The API is hosted on https://api.sorare.com/federation/graphql. The documentation can be found under the Docs section of the GraphQL playground.

You can easily download the GraphQL schema using @apollo/rover:

$ npx -p @apollo/rover rover graph introspect https://api.sorare.com/federation/graphql > schema.graphql

Federated API

Football

The Football-specific resources are either not prefixed or prefixed with So5: Card, Player, So5Fixture, So5Leaderboard, etc. Query fields are available under the football root field.

MLB & NBA

The MLB-specific and NBA-specific resources are prefixed with Baseball and NBA respectively: BaseballCard, BaseballPlayer, BaseballFixture, BaseballLeaderboard, etc.

NFTs & Cards

Each sport has their own Card types. The blockchain cards are also available as sport-agnostic Token types. Each Token belongs to a collectionName that is either football, baseball or nba. For example, the TokenRoot type allows to query offers, auctions and nfts to get Sorare: Football, Sorare: MLB and Sorare: NBA tokens.

It also exposes 2 sport-agnostic subscriptions to get notified about any auctions & offers getting updated:

  • tokenAuctionWasUpdated
  • tokenOfferWasUpdated

While the identifier of a Sorare: Football card used to be its slug, the identifier of a sport-agnostic NFT (Token) is its assetId.

User Authentication

Pre-requisites

To authenticate yourself programmatically through our GraphQL API you'll need:

  • your email
  • the hashed version of your password

Your password needs to be hashed client-side using a salt. The salt can be retrieved with a HTTP GET request against our https://api.sorare.com/api/v1/users/<youremail> endpoint:

Example:

$ curl https://api.sorare.com/api/v1/users/[email protected]

{"salt":"$2a$11$SSOPxn8VSUP90llNuVn.nO"}

The hashed password must be computed with bcrypt:

Example in JavaScript:

import bcrypt from 'bcryptjs';

const hashedPassword = bcrypt.hashSync(password, salt);

Example in Ruby:

require "bcrypt"

hashed_password = BCrypt::Engine.hash_secret(password, salt)

Example in Python:

import bcrypt

hashed_password = bcrypt.hashpw(password, salt)

Please also make sure to set the content-type HTTP header to application/json.

GraphQL signIn mutation

For short and long-lived authentication, you should request a JWT token.

We provide JWT tokens within the signIn mutation. They can be retrieved using the following mutation:

mutation SignInMutation($input: signInInput!) {
  signIn(input: $input) {
    currentUser {
      slug
      jwtToken(aud: "<YourAud>") {
        token
        expiredAt
      }
    }
    errors {
      message
    }
  }
}

It expects the following variables:

{
  "input": {
    "email": "your-email",
    "password": "your-hashed-password"
  }
}

<YourAud> is a mandatory string parameter that identifies the recipients that the JWT is intended for. You can read more about "aud" (Audience) here. We recommend to use an aud reflecting the name of your app - like myappname - to make it easier to debug & track.

$ curl 'https://api.sorare.com/federation/graphql' \
-H 'content-type: application/json' \
-d '{
  "operationName": "SignInMutation",
  "variables": { "input": { "email": "<YourEmail>", "password": "<YourHashPassword>" } },
  "query": "mutation SignInMutation($input: signInInput!) { signIn(input: $input) { currentUser { slug jwtToken(aud: \"<YourAud>\") { token expiredAt } } errors { message } } }"
}'

{"data":{"signIn":{"currentUser":{"slug":"<YourSlug>","jwtToken":{"token":"<YourJWTToken>","expiredAt":"..."}},"errors":[]}}}

You shall then pass the token with an Authorization header alongside a JWT-AUD header to all next API requests:

$ curl 'https://api.sorare.com/federation/graphql' \
-H 'content-type: application/json' \
-H 'Authorization: Bearer <YourJWTToken>' \
-H 'JWT-AUD: <YourAud>' \
-d '{
    "operationName": "CurrentUserQuery",
    "query": "query CurrentUserQuery { currentUser { slug email } }"
}'

{"data":{"currentUser":{"slug":"<YourSlug>","email":"<YourEmail>"}}}

The token will expire after 30 days.

Errors

Please refer to the errors field to understand why a signIn mutation failed.

If currentUser is null and you don't have any errors, it's because the user has 2FA setup. Please follow the next section to handle 2FA signins.

Please note also that if the token has been issued from a specific IP address and you try to generate it from another one, 2FA will automatically activate and you will need the code sent to your email to complete authentication.

2FA

For account with 2FA enabled the signIn mutation will set the otpSessionChallenge field instead of the currentUser one.

mutation SignInMutation($input: signInInput!) {
  signIn(input: $input) {
    currentUser {
      slug
      jwtToken(aud: "<YourAud>") {
        token
        expiredAt
      }
    }
    otpSessionChallenge
    errors {
      message
    }
  }
}

Example:

$ curl 'https://api.sorare.com/federation/graphql' \
-H 'content-type: application/json' \
-d '{
  "operationName": "SignInMutation",
  "variables": { "input": { "email": "<YourEmail>", "password": "<YourHashPassword>" } },
  "query": "mutation SignInMutation($input: signInInput!) { signIn(input: $input) { currentUser { slug jwtToken(aud: \"<YourAud>\") { token expiredAt } } otpSessionChallenge errors { message } } }"
}'

{"data":{"signIn":{"currentUser":null,"otpSessionChallenge":"3a390a0661cd6f4944205f68c13fd04f","errors":[]}}}

In this case, you will need to make another call to the signIn mutation and provide the otpSessionChallenge value you received and a one-time token from your 2FA device as otpAttempt:

{
  "input": {
    "otpSessionChallenge": "eca010be19a80de5c134c324af24c36f",
    "otpAttempt": "788143"
  }
}

Example:

$ curl 'https://api.sorare.com/federation/graphql' \
-H 'content-type: application/json' \
-d '{
  "operationName": "SignInMutation",
  "variables": { "input": { "otpSessionChallenge": "<YourOTPSessionChallenge>", "otpAttempt": "<YourOTPAttemp>" } },
  "query": "mutation SignInMutation($input: signInInput!) { signIn(input: $input) { currentUser { slug jwtToken(aud: \"<YourAud>\") { token expiredAt } } errors { message } } }"
}'

{"data":{"signIn":{"currentUser":{"slug":"<YourSlug>","jwtToken":{"token":"<YourJWTToken>","expiredAt":"..."}},"errors":[]}}}

There is no way currently to revoke the token.

Updated Terms & Conditions

Should the Terms & Conditions of Sorare get updated, you might need to accept them before being able to sign in. Please refer to https://sorare.com/terms_and_conditions to read the latest version of Sorare's terms.

You can accept the terms without being signed in by retrieving the tcuToken returned by the failing signIn mutation with must_accept_tcus error:

mutation SignInMutation($input: signInInput!) {
  signIn(input: $input) {
    currentUser {
      slug
      jwtToken(aud: "<YourAud>") {
        token
        expiredAt
      }
    }
    otpSessionChallenge
    tcuToken
    errors {
      message
    }
  }
}

If the tcuToken is set, you can accept the updated Terms & Conditions with the following mutation:

mutation AcceptTermsMutation($input: acceptTermsInput!) {
  acceptTerms(input: $input) {
    errors {
      message
    }
  }
}

And the following variables:

{
  "input": {
    "acceptTerms": true,
    "acceptPrivacyPolicy": true,
    "acceptGameRules": true,
    "tcuToken": "<YourTcuToken>"
  }
}

Once terms are accepted, you will be able to sign in again.

OAuth Authentication / Login with Sorare

With our OAuth API, users can sign-in to your service using their Sorare account, which allows you to request data on their behalf.

In order to use our OAuth API, we need to issue you a Client ID and Secret for your application. You can request one through our Help Center with the following information:

  • A unique name for your application
  • One or more callback URLs (e.g., http://localhost:3000/auth/sorare/callback for development & https://myapp.com/auth/sorare/callback for production)
  • A logo for your application in PNG format

Sorare currently supports only the following OAuth 2.0 grant flows:

  • Authorization Code
  • Client Credentials

OAuth Credentials

Once we validate your application, you will be provided with:

  • OAuth Client ID
  • OAuth Secret (keep this secret!)

OAuth Scopes

All OAuth applications are provided with one scope which allows access to the following:

  • Basic user information, including their nickname, avatar, and wallet address
  • User's cards, achievements and favorites
  • User's auctions, offers and notifications

The following are not accessible:

  • Email addresses
  • Future lineups and rewards
  • Claiming rewards
  • Bidding, selling, or making offers cards
  • Accepting offers or initiating withdrawals

Access & Refresh Tokens

First you need to create a "Login with Sorare" link in your app and use the following href:

https://sorare.com/oauth/authorize?client_id=<YourUID>&redirect_uri=<YourURLEncodedCallbackURI>&response_type=code&scope=

Once signed in to Sorare, the user will be asked to authorize your app and will ultimately be redirected to your redirect_uri with a ?code= query parameter, for instance https://myapp.com/auth/sorare/callback?code=<YourCode>.

To request an OAuth access token you can then call the https://api.sorare.com/oauth/token endpoint with the following parameters:

  • client_id=<YourOAuthUID>
  • client_secret=<YourOAuthSecret>
  • code=<TheRetrievedCode>
  • grant_type=authorization_code
  • redirect_uri=<TheSameCallbackURIAsBefore>

To refresh an OAuth token you can then call the https://api.sorare.com/oauth/token endpoint with the following parameters:

  • client_id=<YourOAuthUID>
  • client_secret=<YourOAuthSecret>
  • refresh_token=<RefreshToken>
  • grant_type=refresh_token

Example:

$ curl -X POST "https://api.sorare.com/oauth/token" \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'client_id=<YourOAuthUID>&client_secret=<YourOAuthSecret>&code=<TheRetrievedCode>&grant_type=authorization_code&redirect_uri=<TheSameCallbackURIAsBefore>'

{"access_token":"....", "refresh_token": "....", "token_type":"Bearer","expires_in":7200,"scope":"public","created_at":1639608238}

You can then use the access_token the same way you would use a JWT token:

curl 'https://api.sorare.com/federation/graphql' \
-H 'content-type: application/json' \
-H 'Authorization: Bearer <TheUserAccessToken>' \
-d '{
    "operationName": "CurrentUserQuery",
    "query": "query CurrentUserQuery { currentUser { slug } }"
}'

{"data":{"currentUser":{"slug":"<ASlug>"}}}

You can refresh the token

$ curl -X POST "https://api.sorare.com/oauth/token" \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'client_id=<YourOAuthUID>&client_secret=<YourOAuthSecret>&refresh_token=<RefreshToken>&grant_type=refresh_token'

{"access_token":"....", "refresh_token": "....", "token_type":"Bearer","expires_in":7200,"refresh_token":"...","scope":"public","created_at":1639608239}

You can revoke the token

$ curl -X POST "https://api.sorare.com/oauth/revoke" \
 -H 'content-type: application/x-www-form-urlencoded' \
 -d "client_id=<YourOAuthUID>&client_secret=<YourOAuthSecret>&token=<TheUserAccessToken>"

Rate limit

The GraphQL API is rate limited. We can provide an extra API Key on demand that raises those limits.

Here are the configured limits:

  • Unauthenticated API calls: 20 calls per minute
  • Authenticated (JWT or OAuth) API calls: 60 calls per minute
  • API Key API calls: 600 calls per minute

The API key should be passed in an http APIKEY header.

Example:

curl 'https://api.sorare.com/federation/graphql' \
-H 'content-type: application/json' \
-H 'APIKEY: <YourPrivateAPIKey>' \
-H 'Authorization: Bearer <TheUserAccessToken>' \
-d '{
    "operationName": "CurrentUserQuery",
    "query": "query CurrentUserQuery { currentUser { slug } }"
}'

Whenever you perform too many requests, the GraphQL API will answer with a 429 HTTP error code and add a Retry-After: <TimeToWaitInSeconds> header (see RFC) to the response so your code can rely on it to understand how long it should wait before retrying.

⚠️ Queries with NBA/MLB fields are subject to a fixed limit of 150 calls per minute. We're working on making API keys work for these queries as well.

GraphQL Complexity and Depth limits

The GraphQL queries have complexity and depth limits. We can provide extra API keys (on demand) raising those limits.

  • Depth reflects the longest nested fields chain.
  • Complexity reflects the potential total number of fields that would be returned. If the query asks for the first 50 cards, the complexity is computed on 50 cards, even if the result set is composed of 1 card.

We have the following limits:

Depth limit Complexity limit
Anonymous API calls 7 500
Anonymous subscription 7 500
Authenticated API calls 12 30 000
Authenticated subscription 9 1 500

CORS

Our GraphQL API cannot be called from the browser on another domain than the ones we support. Therefore, it's expected to get a Blocked by CORS policy [...]: The ‘Access-Control-Allow-Origin’ header has a value [...] error.

Please consider calling the API from your backend servers.

Pagination

A common use case in GraphQL is traversing the relationship between sets of objects. There are a number of different ways that these relationships can be exposed in GraphQL, giving a varying set of capabilities to the client developer.

Read more about GraphQL pagination on their official documentation.

At Sorare, we use both plural types for connections with a limited cardinality and cursor-based pagination for the others.

A working JavaScript code sample demonstrating how to leverage the cursor to iterate on all cards of a single user is available in examples/allCardsFromUser.js.

Examples

Every operation that involves card or money transfer must be signed with your Starkware private key. It can be exported from sorare.com using your wallet.

Make sure to keep your Private Key secret.

Private key export

To sign with your Starkware private key in JavaScript, we recommend using the JavaScript package @sorare/crypto.

Listing auctions

To list the latest auctions, you can use the following query:

query ListLast10EnglishAuctions {
  transferMarket {
    englishAuctions(last: 10) {
      nodes {
        slug
        currentPrice
        endDate
        bestBid {
          amount
          bidder {
            ... on User {
              nickname
            }
          }
        }
        minNextBid
        cards {
          slug
          name
          rarity
        }
      }
    }
  }
}

A working JavaScript code sample is available in examples/listEnglishAuctions.js.

Bidding on auction

The GraphQL API needs to be called authenticated (see above how to get an Authorization token)

To make a bid on an auction, you need:

  • your Starkware private key
  • the id of the auction you want to bid for
  • the amount you want to bid
  • the exchangeRateId specifying the exchange rate to use to bid

Here are the steps required to bid:

  1. Retrieve the current exchangeRatedId using the config query:
query ConfigQuery {
  config {
    exchangeRate {
      id
    }
  }
}
  1. Get the list of AuthorizationRequest objects from the prepareBid mutation on the auction you want to bid for, with the amount you want to bid:
const prepareBidInput = {
  auctionId: 'EnglishAuction:b50f54a7-752a-4890-ac62-75ee4be78b33',
  amount: '1000000000000000000',
  settlementInfo: {
    currency: 'WEI',
    paymentMethod: 'WALLET',
    exchangeRateId: 'ExchangeRate:a8c74db9-b112-46cf-9c40-6f4ded6c2bb0',
  },
};
mutation PrepareBid($input: prepareBidInput!) {
  prepareBid(input: $input) {
    authorizations {
      fingerprint
      request {
        ...AuthorizationRequestFragment
      }
    }
  }
}
${authorizationRequestFragment}

AuthorizationRequestFragment is defined in authorizations.js.

  1. Sign all AuthorizationRequest objects and build the bidInput argument. buildApprovals is defined in authorizations.js.
const approvals = buildApprovals(starkPrivateKey, authorizations);

const bidInput = {
  approvals,
  auctionId: 'EnglishAuction:b50f54a7-752a-4890-ac62-75ee4be78b33',
  amount: '1000000000000000000',
  settlementInfo: {
    currency: 'WEI',
    paymentMethod: 'WALLET',
    exchangeRateId: 'ExchangeRate:a8c74db9-b112-46cf-9c40-6f4ded6c2bb0',
  },
  clientMutationId: crypto.randomBytes(8).join(''),
};

Note that the clientMutationId is using a random ID.

  1. Call the bid mutation:
mutation Bid($input: bidInput!) {
  tokenBid(input: $input) {
    bid {
      id
    }
    errors {
      message
    }
  }
}

A JavaScript code sample is available in examples/bidAuctionWithEth.js.

Creating offers

The GraphQL API needs to be called authenticated (see above how to get an Authorization token)

To create a Direct or Single Sale offer, you need:

  • your Starkware private key
  • the assetId of the card(s) you want to send and/or receive
  • the amount you want to receive or send
  • the slug of the user you want to make a private offer (Direct Offer only)

Here are the steps required to create an offer:

  1. Build the prepareOfferInput argument:
const prepareOfferInput = {
  type: 'SINGLE_SALE_OFFER',
  sendAssetIds: [
    '0x04002c8934c7fadd5a832a693b8a9d295a915fb1d0c2250d824ae18e7c5bba7a',
  ],
  receiveAssetIds: [],
  receiveAmount: {
    amount: '1000000000000000000', // 1 eth
    currency: 'WEI',
  },
  clientMutationId: crypto.randomBytes(8).join(''),
};

const prepareOfferInput = {
  type: 'DIRECT_OFFER',
  sendAssetIds: [
    '0x04002c8934c7fadd5a832a693b8a9d295a915fb1d0c2250d824ae18e7c5bba7a',
  ],
  receiveAssetIds: [
    'x04003b0dbdf7d5d8037fdf34f0dac9f3a400eddd67df72fff46474fb6b39bb43',
  ],
  sendAmount: {
    amount: '1000000000000000000', // 1 eth
    currency: 'WEI',
  },
  receiverSlug: 'some-user-slug',
  clientMutationId: crypto.randomBytes(8).join(''),
};
  1. Get the list of AuthorizationRequest objects from the prepareOffer mutation:
mutation PrepareOffer($input: prepareOfferInput!) {
  prepareOffer(input: $input) {
    authorizations {
      ...AuthorizationRequestFragment
    }
    errors {
      message
    }
  }
}
${authorizationRequestFragment}

AuthorizationRequestFragment is defined in authorizations.js.

  1. Sign all AuthorizationRequest objects and build the createSingleSaleOfferInput or createDirectOfferInput argument. buildApprovals is defined in authorizations.js.
const approvals = buildApprovals(starkPrivateKey, authorizations);

const createSingleSaleOfferInput = {
  approvals,
  dealId: crypto.randomBytes(8).join(''),
  assetId: '0x04002c8934c7fadd5a832a693b8a9d295a915fb1d0c2250d824ae18e7c5bba7a',
  receiveAmount: {
    amount: '1000', // 10.00 euros
    currency: 'EUR',
  },
  clientMutationId: crypto.randomBytes(8).join(''),
};

const createDirectOfferInput = {
  approvals,
  dealId: crypto.randomBytes(8).join(''),
  sendAssetIds: [
    '0x04002c8934c7fadd5a832a693b8a9d295a915fb1d0c2250d824ae18e7c5bba7a',
  ],
  receiveAssetIds: [
    'x04003b0dbdf7d5d8037fdf34f0dac9f3a400eddd67df72fff46474fb6b39bb43',
  ],
  sendAmount: {
    amount: '1000', // 10.00 euros
    currency: 'EUR',
  },
  receiverSlug: 'some-user-slug',
  clientMutationId: crypto.randomBytes(8).join(''),
};

Note that the clientMutationId and dealId are using random IDs.

  1. Call the createSingleSaleOffer (or createDirectOffer) mutation:
mutation CreateSingleSaleOffer($input: createSingleSaleOfferInput!) {
  createSingleSaleOffer(input: $input) {
    tokenOffer {
      id
    }
    errors {
      message
    }
  }
}

mutation CreateDirectOffer($input: createDirectOfferInput!) {
  createDirectOffer(input: $input) {
    tokenOffer {
      id
    }
    errors {
      message
    }
  }
}

A working JavaScript code sample is available in examples/createSingleSaleOffer.js.

Accepting offers

The GraphQL API needs to be called authenticated (see above how to get an Authorization token)

To accept a Direct or Single Sale offer, you need:

  • your Starkware private key
  • the id of the offer you want to accept
  • the exchangeRateId specifying the exchange rate to use when accepting the offer

Here are the steps required to accept an offer:

  1. Retrieve the current exchangeRatedId using the config query:
query ConfigQuery {
  config {
    exchangeRate {
      id
    }
  }
}
  1. Build the prepareAcceptOfferInput argument:
const prepareAcceptOfferInput = {
  offerId: 'SingleSaleOffer:df241f08-5dee-4cc3-a8f3-b891c9e68c7f',
  settlementInfo: {
    currency: 'WEI',
    paymentMethod: 'WALLET',
    exchangeRateId: 'ExchangeRate:a8c74db9-b112-46cf-9c40-6f4ded6c2bb0',
  },
};
  1. Get the list of AuthorizationRequest objects from the prepareAcceptOffer mutation:
mutation PrepareAcceptOffer($input: prepareAcceptOfferInput!) {
  prepareAcceptOffer(input: $input) {
    authorizations {
      ...AuthorizationRequestFragment
    }
    errors {
      message
    }
  }
}
${authorizationRequestFragment}

AuthorizationRequestFragment is defined in authorizations.js.

  1. Sign all AuthorizationRequest objects and build the acceptOfferInput argument. buildApprovals is defined in authorizations.js.
const approvals = buildApprovals(starkPrivateKey, authorizations);

const acceptOfferInput = {
  approvals,
  offerId: 'SingleSaleOffer:df241f08-5dee-4cc3-a8f3-b891c9e68c7f',
  settlementInfo: {
    currency: 'WEI',
    paymentMethod: 'WALLET',
    exchangeRateId: 'ExchangeRate:a8c74db9-b112-46cf-9c40-6f4ded6c2bb0',
  },
  clientMutationId: crypto.randomBytes(8).join(''),
};

Note that the clientMutationId is using a random ID.

  1. Call the acceptOffer mutation:
mutation AcceptSingleSaleOffer($input: acceptOfferInput!) {
  acceptOffer(input: $input) {
    tokenOffer {
      id
    }
    errors {
      message
    }
  }
}

A working JavaScript code sample is available in examples/acceptSingleSaleOffer.js.

Fetching MLB cards

const slugs = [slug1, slug2];
query GetBaseballCardBySlugs($slugs: [String!]) {
  baseballCards(slugs: $slugs) {
    assetId
    slug
    rarity
    season
    serialNumber
    positions
    team {
      name
    }
    player {
      displayName
    }
  }
}

A working JavaScript code sample is available in examples/getBaseballCard.js.

Fetching the price of an NBA card

const slugs = [slug1, slug2];
query GetNBACardsPrices($slugs: [String!]!) {
  nbaCards(slugs: $slugs)
    token {
      latestEnglishAuction {
        bestBid {
          amount
          amountInFiat { eur gbp usd }
        }
      }
    }
  }
}

A working JavaScript code sample is available in examples/getNBACardPrice.js.

Subscribing to GraphQL events

The Sorare API provides different GraphQL events to subscribe to:

  • aCardWasUpdated: triggers every time a football card is updated. This can be filtered using the following arguments: ages, cardEditions, playerSlugs, positions, owned, rarities, seasonStartYears, serialNumbers, shirtNumbers, slugs
  • currentUserWasUpdated: scoped to the current user, triggers every time the current user is updated (only works when authenticated)
  • gameWasUpdated: triggers every time a game is updated
  • tokenAuctionWasUpdated: triggers every time an auction is updated (football, baseball & nba collections)
  • tokenOfferWasUpdated: triggers every time an offer is updated (football, baseball & nba collections)

The websocket URL to use is wss://ws.sorare.com/cable.

Sorare's GraphQL subscriptions are implemented through websockets with the actioncable-v1-json sub-protocol. Sorare relies on ActionCable because the sorare.com website has been scaled on a Ruby on Rails stack.

JavaScript

In order to ease the websocket + actioncable-v1-json sub-protocoal usage outside of a Ruby on Rails environment, you can use the TypeScript/JavaScript package @sorare/actioncable:

$ yarn add @sorare/actioncable

Football only

const { ActionCable } = require('@sorare/actioncable');

const cable = new ActionCable({
  headers: {
    // 'Authorization': `Bearer <YourJWTorOAuthToken>`,
    // 'APIKEY': '<YourOptionalAPIKey>'
  },
});

cable.subscribe('aCardWasUpdated { slug }', {
  connected() {
    console.log('connected');
  },

  disconnected(error) {
    console.log('disconnected', error);
  },

  rejected(error) {
    console.log('rejected', error);
  },

  received(data) {
    const aCardWasUpdated = data?.result?.data?.aCardWasUpdated;
    if (!aCardWasUpdated) {
      return;
    }
    const { id } = aCardWasUpdated;
    console.log('a card was updated', id);
  },
});

A working JavaScript code sample is available in examples/subscribeAllCardUpdates.js.

Current user updates

The below example will start a subscription for updates on the current user and exit on the first of the following events:

  1. Connection failures or error
  2. An update is received for the current user
const { ActionCable } = require("@sorare/actioncable");

const cable = new ActionCable({
  headers: {
    // 'Authorization': `Bearer <YourJWTorOAuthToken>`,
    // 'APIKEY': '<YourOptionalAPIKey>'
  },
});

cable.subscribe('currentUserWasUpdated { slug nickname }', {
  connected() {
    console.log("connected");
  },

  disconnected(error) {
    console.log("disconnected", error);
    process.exit(1);
  },

  rejected(error) {
    console.log("rejected", error);
    process.exit(1);
  },

  received(data) {
    if (data?.result?.errors?.length > 0) {
      console.log('error', data?.result?.errors);
      process.exit(1);
      return;
    }
    const currentUserWasUpdated = data?.result?.data?.currentUserWasUpdated;
    if (!currentUserWasUpdated) {
      return;
    }
    const { slug } = currentUserWasUpdated;
    console.log('current user was updated', slug);
    process.exit(0);
  }
});

This example can be found in examples/subcribeCurrentUserUpdates.js which can be run with the following environment parameters:

  • JWT_TOKEN
  • JWT_AUD

Football, MLB & NBA tokens

Example of GraphQL subscription to get notified each time an offer is updated:

subscription {
  tokenOfferWasUpdated {
    status
    actualReceiver {
      ... on User {
        slug
      }
    }
    sender {
      ... on User {
        slug
      }
    }
    senderSide {
      wei
      fiat {
        eur
        usd
        gbp
      }
      nfts {
        assetId
        collectionName
      }
    }
    receiverSide {
      wei
      fiat {
        eur
        usd
        gbp
      }
      nfts {
        assetId
        collectionName
        metadata {
          ... on TokenCardMetadataInterface {
            playerSlug
            rarity
            serialNumber
          }
        }
      }
    }
  }
}

Example of GraphQL subscription to get notified each time an auction is updated:

subscription {
  tokenAuctionWasUpdated {
    open
    bestBid {
      amount
      amountInFiat {
        eur
        usd
        gbp
      }
      bidder {
        ... on User {
          slug
        }
      }
    }
    bids {
      nodes {
        amount
        amountInFiat {
          eur
          usd
          gbp
        }
        bidder {
          ... on User {
            slug
          }
        }
      }
    }
    nfts {
      assetId
      collectionName
      metadata {
        ... on TokenCardMetadataInterface {
          playerSlug
          rarity
          serialNumber
        }
      }
    }
  }
}

A working JavaScript code sample is available in examples/subscribeTokenWasUpdated.js.

Python with websocket-client

$ pip3 install websocket-client
import websocket
import json
import time

w_socket = 'wss://ws.sorare.com/cable'
identifier = json.dumps({"channel": "GraphqlChannel"})

subscription_query = {
  "query": "subscription onAnyCardUpdated { aCardWasUpdated { slug } }",
  "variables": {},
  "operationName": "onAnyCardUpdated",
  "action": "execute"
}

def on_open(ws):
  subscribe_command = {"command": "subscribe", "identifier": identifier}
  ws.send(json.dumps(subscribe_command).encode())

  time.sleep(1)

  message_command = {
    "command": "message",
    "identifier": identifier,
    "data": json.dumps(subscription_query)
  }
  ws.send(json.dumps(message_command).encode())

def on_message(ws, data):
  message = json.loads(data)
  type = message.get('type')
  if type == 'welcome':
    pass
  elif type == 'ping':
    pass
  elif message.get('message') is not None:
    print(message['message'])

def on_error(ws, error):
  print('Error:', error)

def on_close(ws, close_status_code, close_message):
  print('WebSocket Closed:', close_message, close_status_code)

def long_connection():
  ws = websocket.WebSocketApp(
    w_socket,
    on_message=on_message,
    on_close=on_close,
    on_error=on_error,
    on_open=on_open
  )
  ws.run_forever()

if __name__ == '__main__':
  long_connection()

A working Python3 code sample is available in examples/subscribe_all_card_updates.py.

Python with graphql-python/gql

Using the gqlactioncable package, it is now possible to make subscriptions using graphql-python/gql.

$ pip install gqlactioncable
import asyncio

from gql import Client, gql

from gqlactioncable import ActionCableWebsocketsTransport


async def main():

    transport = ActionCableWebsocketsTransport(
        url="wss://ws.sorare.com/cable",
    )

    async with Client(transport=transport) as session:

        subscription = gql(
            """
            subscription onAnyCardUpdated {
              aCardWasUpdated {
                slug
              }
            }
        """
        )

        async for result in session.subscribe(subscription):
            print(result)


asyncio.run(main())

This example is available in examples/gql_subscription_all_cards.py.

See also an example for http queries with gql: examples/gql_query_all_cards.py.

api's People

Contributors

redox avatar such avatar eulbat avatar utay avatar antoine-malliarakis avatar sfriquet avatar piedup avatar mk-dhia avatar thom-des avatar kaderate avatar davidbyttow avatar thorizerftw avatar hugo-hache avatar spinach avatar annelauresorare avatar ayoakala avatar hadevmin avatar leszekhanusz avatar haoliangz avatar jorissorare avatar damienpradier avatar jmaupoux avatar omahs avatar sofairbot avatar

Stargazers

HamDogJones avatar

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.