Faits :
A.
Par jugement du 7 septembre 2020, le Tribunal correctionnel de l'arrondissement de Lausanne a reconnu A._ coupable d'infraction grave à la LStup (RS 812.121), l'a condamné à une peine privative de liberté de 26 mois et a ordonné son expulsion du territoire suisse pour une durée de sept ans. Il a par ailleurs statué sur le sort des objets séquestrés, les frais et les indemnités.
B.
Par jugement du 27 janvier 2021, la Cour d'appel pénale du Tribunal cantonal vaudois a partiellement admis l'appel formé par A._ contre ce jugement. Elle a condamné A._ à une peine privative de liberté de 24 mois, assortie du sursis et d'un délai d'épreuve de trois ans, et confirmé le jugement pour le surplus.
En substance, la décision cantonale repose sur les faits suivants.
Dans la région de U._, entre le début de l'année 2017 et le mois d'avril 2017, A._ s'est livré à un trafic de cocaïne et de MDMA. Entre le mois de février 2017 et le début du mois d'avril 2017, il a vendu 110 grammes de cocaïne destinés à la revente à B._, déféré séparément, par 20 grammes au prix de 80 fr. le gramme, ainsi que 10 grammes de MDMA destinés à la revente au prix de 70 fr. le gramme.
Lors de l'interpellation de B._, le 7 avril 2017, celui-ci détenait encore 70 grammes de la cocaïne que A._ lui avait vendue à des fins de revente, soit 30 grammes saisis sur lui et conditionnés en 10 sachets minigrips d'un gramme et 20 parachutes d'un gramme, ainsi que 40 grammes nets découverts dans trois Tupperware lors de la perquisition effectuée à son domicile. En outre, B._ détenait 9,3 grammes de MDMA sous forme de cristaux, soit 8 grammes saisis sur lui et conditionnés en petits sachets minigrips d'un gramme, le solde découvert lors de la perquisition effectuée à son domicile. Un parachute jaune contenant de la cocaïne, échantillon de la drogue saisie sur B._ lors de son interpellation, ainsi qu'un morceau de cocaïne compacte, échantillon de la cocaïne saisie à son domicile, ont été transmis à l'École des sciences criminelles (ESC) pour analyses. La cocaïne contenue dans le parachute jaune présentait un taux de pureté de 83,9% correspondant à une masse nette pure de cocaïne de 0,7 gramme net. Le morceau de cocaïne présentait un taux de pureté de 53,7% correspondant à une masse nette pure de cocaïne de 0,7 gramme net. Par conséquent, les 30 grammes de cocaïne saisis sur B._ représentaient 25,1 grammes de cocaïne pure. Quant aux 40 grammes retrouvés lors de la perquisition au domicile de celui-ci, ils représentaient 21,4 grammes de cocaïne pure. Ainsi, la drogue saisie sur lui et qui lui a été vendue par A._ représentait un total de 46,5 grammes de cocaïne pure.
Lors de la perquisition effectuée le 3 août 2017 dans le second logement de A._, sis à l'avenue V._ à U._, y ont été découverts trois parachutes de MDMA sous forme de cristaux d'un poids total de 3,6 grammes, correspondant à une masse de substance pure de 2,9 grammes, un morceau de 5,8 grammes de haschich et un sachet minigrip contenant 1,5 gramme de marijuana.
C.
A._ forme un recours en matière pénale et un recours constitutionnel subsidiaire au Tribunal fédéral contre le jugement précité. Il conclut, avec suite de frais et dépens, principalement à l'admission de son recours en matière pénale et à la réforme du jugement attaqué, en ce sens qu'il est acquitté du chef d'infraction grave à la LStup. Subsidiairement, il conclut à l'annulation de la décision entreprise et au renvoi de la cause à l'autorité cantonale pour nouvelle instruction et nouvelle décision dans le sens des considérants. Par ailleurs, il requiert l'octroi de l'assistance judiciaire. Sans y conclure formellement, le recourant demande, subsidiairement, l'admission de son recours constitutionnel subsidiaire.
Considérant en droit :
1.
Dirigé contre une décision rendue en matière pénale (cf. art. 78 LTF) - revêtant un caractère final (cf. art. 90 LTF) - par une autorité cantonale de dernière instance (cf. art. 80 LTF), le recours en matière pénale est en principe recevable quant à son objet. Le recours constitutionnel subsidiaire qu'entend également déposer le recourant est par conséquent exclu (cf. art. 113 LTF).
2.
Dénonçant une violation de la maxime d'instruction et des art. 308 et 343 CPP, le recourant se plaint de ce qu'il n'aurait jamais été confronté à B._, qui avait seulement été entendu par la police, dans une affaire distincte, antérieure à l'ouverture de la présente procédure, alors qu'il appartenait aux autorités d'entendre d'office un "témoin essentiel". Par ailleurs, il semble se plaindre de ce qu'aucune mesure d'instruction "de nature scientifique" n'aurait été diligentée afin de comparer la drogue saisie chez lui à celle saisie chez B._.
Le recourant formule ses critiques pour la première fois devant le Tribunal fédéral. A tout le moins ne prétend-il pas les avoir formulées devant la cour cantonale, ni que celle-ci aurait violé son droit d'être entendu en ne les traitant pas. S'agissant de griefs liés à la conduite de la procédure et à la méthodologie de l'instruction, ceux-ci sont irrecevables sous l'angle du principe de la bonne foi, lequel interdit de saisir les juridictions supérieures d'un éventuel vice qui aurait pu être invoqué dans une phase antérieure du procès (ATF 143 IV 397 consid. 3.4.2 p. 405 s.). Ils sont irrecevables faute d'épuisement des instances cantonales (cf. art. 80 al. 1 LTF).
3.
Invoquant l'interdiction de l'arbitraire et le principe " in dubio pro reo ", le recourant critique sa condamnation pour infraction grave à la LStup (art. 19 al. 1 let. b, c, d et g et al. 2 let. a LStup).
3.1. Le Tribunal fédéral n'est pas une autorité d'appel, auprès de laquelle les faits pourraient être rediscutés librement. Il est lié par les constatations de fait de la décision entreprise (art. 105 al. 1 LTF), à moins qu'elles n'aient été établies en violation du droit ou de manière manifestement inexacte au sens des art. 97 al. 1 et 105 al. 2 LTF, à savoir, pour l'essentiel, de façon arbitraire au sens de l'art. 9 Cst. Une décision n'est pas arbitraire du seul fait qu'elle apparaît discutable ou même critiquable; il faut qu'elle soit manifestement insoutenable et cela non seulement dans sa motivation mais aussi dans son résultat (ATF 146 IV 88 consid. 1.3.1 p. 91 s.; 145 IV 154 consid. 1.1 p. 155 s.; 143 IV 500 consid. 1.1 p. 503; sur la notion d'arbitraire v. ATF 143 IV 241 consid. 2.3.1 p. 244). Le Tribunal fédéral n'entre en matière sur les moyens fondés sur la violation de droits fondamentaux, dont l'interdiction de l'arbitraire, que s'ils ont été invoqués et motivés de manière précise (art. 106 al. 2 LTF; ATF 143 IV 500 consid. 1.1 p. 503). Les critiques de nature appellatoire sont irrecevables (ATF 146 IV 114 consid. 2.1 p. 118; 146 IV 88 consid. 1.3.1 p. 92; 145 IV 154 consid. 1.1 p. 156). Lorsque l'appréciation des preuves et la constatation des faits sont critiquées en référence à la présomption d'innocence (art. 6 par. 2 CEDH, 32 al. 1 Cst. et 10 CPP), le principe "in dubio pro reo" n'a pas de portée plus large que l'interdiction de l'arbitraire (ATF 146 IV 88 consid. 1.3.1 p. 92; 145 IV 154 consid. 1.1 p. 156; 143 IV 500 consid. 1.1 p. 503).
Lorsque l'autorité cantonale a forgé sa conviction quant aux faits sur la base d'un ensemble d'éléments ou d'indices convergents, il ne suffit pas que l'un ou l'autre de ceux-ci ou même chacun d'eux pris isolément soit à lui seul insuffisant. L'appréciation des preuves doit en effet être examinée dans son ensemble. Il n'y a ainsi pas d'arbitraire si l'état de fait retenu pouvait être déduit de manière soutenable du rapprochement de divers éléments ou indices. De même, il n'y a pas d'arbitraire du seul fait qu'un ou plusieurs arguments corroboratifs apparaissent fragiles, si la solution retenue peut être justifiée de façon soutenable par un ou plusieurs arguments de nature à emporter la conviction (arrêts 6B_31/2021 du 7 avril 2022 consid. 2.1.2; 6B_732/2021 du 24 février 2022 consid. 2.1; 6B_1189/2021 du 16 février 2022 consid. 3.1).
3.2. La cour cantonale, qui a retenu les faits constatés dans l'acte d'accusation, a considéré que les indices du dossier, qui allaient tous dans le même sens, constituaient, ensemble, le fondement de la culpabilité du recourant. Elle s'est d'abord référée au jugement du tribunal correctionnel du 7 septembre 2020 (p. 12 à 14), qui avait retenu la culpabilité du recourant sur la base de la mise en cause du recourant par B._, dont les déclarations étaient claires, constantes et précises, le résultat de l'exploration du téléphone portable du recourant, ainsi que les résultats des analyses scientifiques de la drogue saisie au domicile du recourant, dont la qualité et la forme étaient identiques à celle retrouvée chez B._. La cour cantonale a ensuite précisé certains points en lien avec les griefs formulés par le recourant. En résumé, elle a observé que le fait que B._ ait été entendu comme prévenu dans une cause dirigée contre lui n'était pas de nature à faire douter de ses déclarations puisqu'il s'était incriminé lui-même, qu'il avait été condamné sur la base de ses déclarations uniquement et que les faits non admis n'avaient pas été retenus à son encontre; dans ces circonstances, peu importait qu'il ait été entendu en qualité de prévenu, plutôt que de "témoin", lui-même ne tirant aucun avantage direct de ses déclarations. B._ s'était certes exprimé une seule fois sur les faits, durant l'enquête, mais sa déposition, claire et précise, ne mettait pas en cause uniquement le recourant mais également un tiers comme fournisseur d'ecstasy; B._ s'était auto-incriminé en s'exprimant sur les ventes en cours, admettant ainsi lui-même la commission de faits relevant de l'infraction grave à la LStup. Le recourant ne prenait pas position sur les développements du tribunal correctionnel, à teneur desquels B._ avait expliqué qu'il contactait le recourant via le raccordement xxx, établi au nom d'une identité fictive ("C._"), ce qui correspondait à un stratagème utilisé par ceux se livrant à des trafics de stupéfiants. Il était singulier que le recourant ait donné à B._ un numéro qui avait principalement eu des contacts avec un seul autre utilisateur en Suisse, soit D._, lui aussi mêlé à des affaires de stupéfiants. Cet élément ajouté à la crédibilité des déclarations de B._ et à l'usage d'une identité fictive pour l'achat de la carte SIM plaidait en faveur de conversations illicites. La qualité et la forme de la drogue retrouvée chez le recourant et chez B._ étaient identiques, ce qui allait également dans le sens d'une culpabilité. Qu'il n'y ait pas eu de versements sur le compte bancaire du recourant n'excluait pas la remise d'argent au recourant. L'ADN du recourant n'avait certes pas été trouvé sur la drogue dont B._ était possesseur, aucune information concernant un éventuel prélèvement ADN ne figurant au dossier; l'implication du recourant était établie par d'autres éléments.
3.3. Le recourant s'en prend d'abord aux déclarations de B._, lesquelles seraient floues et, partant, impropres à établir les "circonstances de faits" essentielles. Aucune preuve ne permettrait de donner plus de crédit aux déclarations de B._ qu'aux siennes. Les éléments du dossier ne permettraient pas de conclure que B._ se serait fourni chez le recourant. L'éventualité que les drogues consommées par le recourant et B._ proviennent d'une autre source commune aurait dû être pris en compte comme "état de fait alternatif".
Une telle argumentation relève d'une libre discussion des faits, le recourant se contentant d'opposer sa propre appréciation des preuves à celle de l'autorité cantonale et à formuler des hypothèses, dans une démarche purement appellatoire. Il n'explique en revanche pas en quoi il était arbitraire de conclure, sur la base du fait que la qualité et la forme (cristaux) de la drogue trouvée chez le recourant et chez B._ étaient identiques, que cette constatation constituait un indice supplémentaire d'une même provenance de la drogue, et donc de la culpabilité du recourant. Le recourant fait grand cas de ce que la cour cantonale aurait prêté une portée décisive aux déclarations de B._, de manière arbitraire. Il ressort du jugement entrepris que la cour cantonale ne s'est pas uniquement basée sur les déclarations de l'intéressé pour fonder la culpabilité du recourant, mais qu'elle s'est également référée à d'autres éléments. Elle a notamment pris en compte les résultats des analyses scientifiques de la drogue saisie au domicile du recourant, dont la qualité et la forme étaient identiques à celle retrouvée chez B._. Elle a également apprécié les éléments résultant de l'exploration du téléphone portable du recourant, qu'il avait utilisé pour contacter B._. A cet égard, elle a constaté qu'il s'agissait d'un raccordement établi au nom d'une identité fictive, lequel avait eu des contacts avec un seul autre utilisateur en Suisse, lui aussi mêlé à du trafic de stupéfiants, soulignant la singularité de ces faits. Dès lors, le recourant échoue à démontrer en quoi la cour cantonale aurait versé dans l'arbitraire. Les griefs sont partant rejetés, dans la mesure de leur recevabilité.
Le recourant soutient encore que les déclarations de B._ auraient dû être appréciées avec réserve, dans la mesure où celui-ci serait lui-même impliqué dans un trafic de drogue et qu'il aurait tout intérêt à incriminer le recourant. La cour cantonale a exposé les motifs pour lesquels elle estimait que la qualité de prévenu de l'intéressé dans une procédure conduite séparément n'était pas de nature à faire douter de ses déclarations. A cet égard, elle a relevé que B._ s'était incriminé lui-même puisqu'il avait admis des faits relevant d'une infraction grave à la LStup, lesquels avaient conduits à sa condamnation. Par ailleurs, ses déclarations ne mettaient pas seulement en cause le recourant mais aussi un tiers. Le recourant ne s'en prend pas à la motivation cantonale. Il ne développe aucune argumentation précise tendant à démontrer, eu égard aux exigences accrues de motivation de l'art. 106 al. 2 LTF, en quoi les considérations cantonales seraient arbitraires. Insuffisamment motivé, son grief est irrecevable.
3.4. Le recourant soutient que les contrôles rétroactifs du raccordement xxx n'auraient pas apporté d'informations pertinentes selon les enquêteurs, élément qui aurait été écarté par la cour cantonale alors qu'il s'agissait d'un point à décharge.
Dans la mesure où il se fonde sur des faits qui ne ressortent pas du jugement attaqué, sans démontrer en quoi ceux-ci auraient fait l'objet d'une omission arbitraire, le grief est irrecevable. Au demeurant, en prétendant que les contrôles rétroactifs n'auraient apporté aucun élément probant, le recourant ne fait qu'opposer sa propre appréciation à celle de la cour cantonale dans une démarche purement appellatoire, partant irrecevable. Il ne démontre ainsi pas en quoi la cour cantonale aurait versé dans l'arbitraire en considérant notamment qu'il était singulier que le recourant ait donné à B._ un numéro qui avait eu des contacts avec un seul autre utilisateur en Suisse, lui aussi mêlé à des affaires de stupéfiants. En affirmant encore que l'acquisition d'une carte SIM sous un faux nom ne serait "absolument pas" une preuve de culpabilité d'un trafic de drogue, qu'il serait établi qu'il avait utilisé le raccordement à des fins licites ou que le fait qu'il aurait eu des contacts téléphoniques avec B._ ne permettrait pas de prouver que ces échanges avaient pour objet la vente de stupéfiants, le recourant procède une nouvelle fois de manière appellatoire. Ses griefs sont irrecevables.
3.5. Le recourant soutient enfin que l'appréciation des "autres" moyens de preuve serait entachée d'un "biais de confirmation", dans la mesure où "ils" auraient été analysés uniquement dans la perspective de corroborer le postulat de véracité des déclarations de B._. Le recourant se limite à proposer sa lecture personnelle des preuves, de manière appellatoire, partant irrecevable.
Le recourant soutient que la cour cantonale aurait retenu des éléments "objectivement erronés", puisque, sur la base d'un SMS envoyé le 2 août 2016 dans lequel il était question d'un café hollandais, les inspecteurs auraient affirmé que le recourant se trouvait alors aux Pays-Bas, ce que celui-ci aurait omis de leur dire. Il prétend que le SMS en question aurait été envoyé le 2 août 2017, date à laquelle il ne se trouvait pas à l'étranger. On peine à comprendre ce que le recourant entend démontrer par cette affirmation, au demeurant purement appellatoire. Quoi qu'il en soit, il se base sur des faits qui ne ressortent pas du jugement cantonal, sans démontrer en quoi ceux-ci auraient fait l'objet d'une omission arbitraire, de sorte qu'ils sont irrecevables.
Pour le reste, en tant que le recourant se borne à renvoyer à sa déclaration d'appel, ce procédé n'est pas admissible (ATF 143 IV 122 consid. 3.3).
4.
Le recourant semble contester son expulsion. Il ne formule cependant aucun grief recevable (art. 42 al. 2 LTF) tiré d'une violation de l'art. 66a CP, disposition qu'il n'invoque même pas.
5.
Le recours doit être rejeté dans la mesure où il est recevable. Comme il était dénué de chance de succès, la demande d'assistance judiciaire doit être rejetée (art. 64 al. 1 LTF). Le recourant, qui succombe, supporte les frais judiciaires, dont le montant sera fixé en tenant compte de sa situation financière, laquelle n'apparaît pas favorable (art. 65 al. 2 et 66 al. 1 LTF).
Par ces motifs, le Tribunal fédéral prononce :
1.
Le recours est rejeté, dans la mesure où il est recevable.
2.
La demande d'assistance judiciaire est rejetée.
3.
Les frais judiciaires, arrêtés à 1'200 fr., sont mis à la charge du recourant.
4.
Le présent arrêt est communiqué aux parties et à la Cour d'appel pénale du Tribunal cantonal du canton de Vaud.
Lausanne, le 5 mai 2022
Au nom de la Cour de droit pénal
du Tribunal fédéral suisse
Le Juge présidant : Denys
La Greffière : Rettby